Skip to content

Commit c80b9f3

Browse files
authored
Merge pull request #386 from TypeCellOS/staging
deploy
2 parents 9168efb + a9bd795 commit c80b9f3

File tree

74 files changed

+13989
-5338
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+13989
-5338
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ To run the project, open the command line in the project's root directory and en
4444

4545
The above `npm start` executes the `vite dev` command of `packages/editor` and watches for changes to this main package.
4646

47-
## Watch changes
47+
<!-- ## Watch changes
4848
4949
npm run watch
5050
51-
You might also be making changes to other packages in the `packages` directory. To continuously watch and compile for changes, open a new terminal and run `npm run watch`.
51+
You might also be making changes to other packages in the `packages` directory. To continuously watch and compile for changes, open a new terminal and run `npm run watch`. -->
5252

5353
## Testing
5454

package-lock.json

Lines changed: 7705 additions & 4455 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,14 @@
2626
"patch-package": "patch-package",
2727
"postinstall": "patch-package",
2828
"playwright:dev": "npm run playwright:dev --workspaces",
29-
"playwright:preview": "npm run playwright:preview --workspaces",
29+
"playwright:preview": "npm run playwright:preview --workspace=packages/editor",
3030
"install-playwright": "npx playwright install --with-deps",
3131
"test": "npm run test --workspaces",
3232
"unittest:vitest": "npm run unittest:vitest --workspaces",
3333
"wip:unittest:vitest:coverage": "vitest run --coverage -r packages/xxx",
3434
"build": "npm run build --workspaces",
3535
"build:react": "npm run build:react --workspace=packages/editor",
3636
"lint": "npm run lint --workspaces",
37-
"watch": "npm run build && npm run --parallel watch",
3837
"start": "npm run start-react",
3938
"start-react": "npm run start --workspace=packages/editor",
4039
"start:preview": "npm run preview --workspace=packages/editor",

packages/editor/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
"private": true,
55
"dependencies": {
66
"react-confetti-explosion": "^2.1.2",
7-
"@hocuspocus/provider": "^2.4.0",
7+
"@hocuspocus/provider": "2.4.0",
88
"@atlaskit/atlassian-navigation": "^2.6.13",
99
"@atlaskit/avatar": "^21.3.7",
1010
"@atlaskit/breadcrumbs": "^11.10.5",
1111
"@atlaskit/button": "^16.8.2",
1212
"@atlaskit/css-reset": "^6.5.3",
13+
"@atlaskit/checkbox": "^13.3.0",
1314
"@atlaskit/dropdown-menu": "^11.10.5",
1415
"@atlaskit/empty-state": "^7.6.3",
1516
"@atlaskit/flag": "^15.2.15",
@@ -26,7 +27,7 @@
2627
"@atlaskit/textfield": "^5.6.3",
2728
"@atlaskit/tree": "^8.8.5",
2829
"@tiptap/core": "^2.0.4",
29-
"@blocknote/core": "^0.9.3",
30+
"@blocknote/core": "^0.13.2",
3031
"@emotion/react": "^11.4.0",
3132
"@supabase/auth-ui-react": "^0.4.5",
3233
"@supabase/auth-ui-shared": "^0.1.7",
@@ -57,14 +58,15 @@
5758
"vscode-lib": "^0.1.2",
5859
"web-vitals": "^1.0.1",
5960
"y-indexeddb": "9.0.6",
61+
"y-websocket": "^2.0.3",
6062
"y-protocols": "^1.0.5",
6163
"yjs": "^13.6.4",
6264
"react-inspector": "^6.0.1"
6365
},
6466
"scripts": {
6567
"copytypes:self": "tsc --declaration --emitDeclarationOnly --noEmit false --composite false --declarationDir ./public/types/@typecell-org/editor",
6668
"copytypes:externaldep": "mkdir -p public/types/$npm_config_pkgname && cp -rf ../../node_modules/$npm_config_pkgname/. public/types/$npm_config_pkgname",
67-
"copytypes:allexternaldeps": "npm run copytypes:externaldep --pkgname=@types/react && npm run copytypes:externaldep --pkgname=@types/scheduler && npm run copytypes:externaldep --pkgname=@types/prop-types && npm run copytypes:externaldep --pkgname=csstype",
69+
"copytypes:allexternaldeps": "npm run copytypes:externaldep --pkgname=@types/react && npm run copytypes:externaldep --pkgname=@types/prop-types && npm run copytypes:externaldep --pkgname=csstype",
6870
"copytypes:dep": "mkdir -p public/types/@typecell-org/$npm_config_pkgname && cp -rf ../$npm_config_pkgname/types/. public/types/@typecell-org/$npm_config_pkgname",
6971
"copytypes:alldeps": "npm run copytypes:dep --pkgname=util && npm run copytypes:dep --pkgname=engine && npm run copytypes:dep --pkgname=frame",
7072
"copytypes": "npm run copytypes:self && npm run copytypes:alldeps && npm run copytypes:allexternaldeps",

packages/editor/public/_docs/index.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"manual/3. Reactive variables.md",
88
"manual/4. Inputs.md",
99
"manual/5. Imports and NPM.md",
10+
"manual/6. Plugins.md",
1011
"README.md"
1112
]
1213
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Plugins
2+
3+
A powerful concept that TypeCell explores is _End User Programming_. In TypeCell, it's possible to customize the program you're using (a Notion-style document editor) with new capabilities, _right from within the application itself_. This means you can modify the way TypeCell works, without changing the source code, but just by creating new code in TypeCell code blocks.
4+
5+
## Map block
6+
7+
When using software like Notion, Google Docs, or Word, you're limited to the blocks they provide (paragraphs, images, lists, tables, etc.). What if you want to add an interactive map, or chart to your document? Let's explore how this can be done in TypeCell.
8+
9+
Let's first set up the code to render a Map, based on _react-map-gl_.
10+
11+
### Map code
12+
13+
First, let's set up some reactive variables for our map component:
14+
15+
```typescript
16+
export let zoom = 1;
17+
export let latitude = 1;
18+
export let longitude = 1;
19+
export let markers: Array<{
20+
latitude: number;
21+
longitude: number;
22+
color: string;
23+
text: string;
24+
}> = [];
25+
```
26+
27+
And import the required CSS stylesheet:
28+
29+
```typescript
30+
import css from "maplibre-gl/dist/maplibre-gl.css";
31+
export { css };
32+
```
33+
34+
Now, let's create the main code that renders our map component:
35+
36+
```typescript
37+
import MapLibre, { Marker, Source, Layer, Popup } from "react-map-gl/maplibre";
38+
import maplibregl from "maplibre-gl";
39+
40+
export const map = (
41+
<div style={{ width: 700, height: 400 }}>
42+
<MapLibre
43+
onMove={(e) => {
44+
$.zoom = e.viewState.zoom;
45+
$.latitude = e.viewState.latitude;
46+
$.longitude = e.viewState.longitude;
47+
}}
48+
longitude={$.longitude}
49+
latitude={$.latitude}
50+
zoom={$.zoom}
51+
mapStyle="https://demotiles.maplibre.org/style.json">
52+
{$.markers.map((m, i) => (
53+
<Marker
54+
key={i}
55+
latitude={m.latitude}
56+
longitude={m.longitude}
57+
color={m.color}
58+
popup={m.text ? new maplibregl.Popup().setText(m.text) : undefined}
59+
/>
60+
))}
61+
</MapLibre>
62+
</div>
63+
);
64+
```
65+
66+
### Register a plugin
67+
68+
Now, we can register the _Map_ variable as a Block that can be added to any document. Try it out by typing "/" in this document, or clicking the + icon next to a block. You'll see that you can now add Map blocks to the document.
69+
70+
```typescript
71+
// Plugin registration
72+
typecell.editor.registerBlock({
73+
name: "Map",
74+
blockVariable: "map",
75+
// Variables for properties screen that's auto-generated
76+
// and shows when clicking the settings-gear icon
77+
settings: {
78+
latitude: true,
79+
longitude: true,
80+
zoom: true,
81+
},
82+
});
83+
```
84+
85+
Your local environment will keep track of registered plugins. Now that you've visited this page, you can reuse the Plugin registered here in any other document you create. Try this out by signing in, and going to a document in your own workspace. Access the document / plugin menu via the top-right dots.

packages/editor/src/app/documentRenderers/richtext/FrameHost.tsx

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,43 +65,54 @@ export function FrameHost(props: {
6565
>();
6666

6767
const methods = {
68+
markPlugins: async (identifierStr: string, value: boolean) => {
69+
const identifier = parseIdentifier(identifierStr);
70+
props.sessionStore.documentCoordinator?.markPlugins(identifier, value);
71+
},
6872
processYjsMessage: async (message: ArrayBuffer) => {
6973
provider.onMessage(message, "penpal");
7074
},
71-
registerTypeCellModuleCompiler: async (moduleName: string) => {
72-
if (moduleManagers.has(moduleName)) {
73-
console.warn("already has moduleManager for", moduleName);
74-
return;
75-
}
75+
resolveModuleName: async (moduleName: string) => {
7676
if (!moduleName.startsWith("!")) {
7777
throw new Error("invalid module name");
7878
}
79-
const identifierStr = moduleName.substring(1);
79+
80+
const identifier = parseIdentifier(moduleName.substring(1));
81+
const identifierStr = identifier.toString();
82+
return identifierStr;
83+
},
84+
registerTypeCellModuleCompiler: async (identifierStr: string) => {
8085
const identifier = parseIdentifier(identifierStr);
86+
if (moduleManagers.has(identifierStr)) {
87+
console.warn("already has moduleManager for", identifierStr);
88+
return identifierStr;
89+
}
90+
8191
const provider = new DocumentResourceModelProvider(
8292
identifier,
8393
props.sessionStore,
8494
);
8595

8696
const forwarder = new ModelForwarder(
87-
"modules/" + moduleName,
97+
"modules/" + identifierStr,
8898
provider,
8999
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
90100
connectionMethods.current!,
91101
);
92-
moduleManagers.set(moduleName, { provider, forwarder });
102+
103+
moduleManagers.set(identifierStr, { provider, forwarder });
93104
await forwarder.initialize();
94-
return identifier.toString();
105+
return identifierStr;
95106
},
96-
unregisterTypeCellModuleCompiler: async (moduleName: string) => {
97-
const moduleManager = moduleManagers.get(moduleName);
107+
unregisterTypeCellModuleCompiler: async (identifierStr: string) => {
108+
const moduleManager = moduleManagers.get(identifierStr);
98109
if (!moduleManager) {
99-
console.warn("no moduleManager for", moduleName);
110+
console.warn("no moduleManager for", identifierStr);
100111
return;
101112
}
102113
moduleManager.provider.dispose();
103114
moduleManager.forwarder.dispose();
104-
moduleManagers.delete(moduleName);
115+
moduleManagers.delete(identifierStr);
105116
},
106117
};
107118

packages/editor/src/app/main/components/ProfilePopup.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import { toProfilePage } from "../../routes/routes";
1111

1212
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1313
const Trigger = observer((props: any) => {
14-
const { triggerRef, isSelected, testId, ...passProps } = props;
14+
const { triggerRef, isSelected, testId, sessionStore, ...passProps } = props;
1515
return (
1616
<Profile
1717
testId="profile-button"
1818
icon={
1919
<Avatar
20-
name={props.sessionStore.loggedInUserId}
21-
src={props.sessionStore.profile?.avatar_url || undefined}
20+
name={sessionStore.loggedInUserId}
21+
src={sessionStore.profile?.avatar_url || undefined}
2222
size="32"
2323
round={true}
2424
textSizeRatio={2}

packages/editor/src/app/main/components/documentMenu/DocumentMenu.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ import { DocumentResource } from "../../../../store/DocumentResource";
1212
import { SessionStore } from "../../../../store/local/SessionStore";
1313
import {
1414
ClosePermissionsDialog,
15+
ClosePluginDialog,
1516
IsPermissionsDialogOpen,
17+
IsPluginDialogOpen,
1618
OpenPermissionsDialog,
19+
OpenPluginDialog,
1720
} from "../../../routes/routes";
1821
import { MenuBar } from "../menuBar/MenuBar";
1922

@@ -26,6 +29,7 @@ import SupabasePermissionsDialog from "../../../supabase-auth/routes/permissions
2629
import { Breadcrumb } from "./Breadcrumb";
2730
import styles from "./DocumentMenu.module.css";
2831
import { ForkAlert } from "./ForkAlert";
32+
import PluginDialog from "./PluginDialog";
2933
import { ShareButton } from "./ShareButton";
3034

3135
type Props = {
@@ -36,7 +40,7 @@ type Props = {
3640
// TODO: move?
3741
function userCanEditPermissions(
3842
sessionStore: SessionStore,
39-
identifier: Identifier
43+
identifier: Identifier,
4044
) {
4145
if (identifier instanceof HttpsIdentifier) {
4246
return false;
@@ -56,7 +60,7 @@ export const DocumentMenu: React.FC<Props> = observer((props) => {
5660
const { sessionStore } = props;
5761
const canEditPermissions = userCanEditPermissions(
5862
sessionStore,
59-
props.document.identifier
63+
props.document.identifier,
6064
);
6165
const location = useLocation();
6266
const navigate = useNavigate();
@@ -127,13 +131,27 @@ export const DocumentMenu: React.FC<Props> = observer((props) => {
127131
Permissions
128132
</DropdownItem>
129133
)}
134+
{props.document instanceof DocumentResource && (
135+
<DropdownItem onClick={() => OpenPluginDialog(navigate)}>
136+
Plugins
137+
</DropdownItem>
138+
)}
130139
</DropdownMenu>
131140
</li>
132141
</>
133142
)}
134143
</ul>
135144
</aside>
136145
{canEditPermissions && permissionsArea}
146+
{props.document instanceof DocumentResource && (
147+
<PluginDialog
148+
close={() => ClosePluginDialog(navigate)}
149+
isOpen={IsPluginDialogOpen(location)}
150+
identifier={props.document.identifier}
151+
sessionStore={sessionStore}
152+
document={props.document}
153+
/>
154+
)}
137155
</MenuBar>
138156
);
139157
});

0 commit comments

Comments
 (0)