Skip to content

Commit

Permalink
Merge pull request #386 from TypeCellOS/staging
Browse files Browse the repository at this point in the history
deploy
  • Loading branch information
YousefED authored May 22, 2024
2 parents 9168efb + a9bd795 commit c80b9f3
Show file tree
Hide file tree
Showing 74 changed files with 13,989 additions and 5,338 deletions.
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ To run the project, open the command line in the project's root directory and en

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

## Watch changes
<!-- ## Watch changes
npm run watch
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`.
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`. -->

## Testing

Expand Down
12,160 changes: 7,705 additions & 4,455 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,14 @@
"patch-package": "patch-package",
"postinstall": "patch-package",
"playwright:dev": "npm run playwright:dev --workspaces",
"playwright:preview": "npm run playwright:preview --workspaces",
"playwright:preview": "npm run playwright:preview --workspace=packages/editor",
"install-playwright": "npx playwright install --with-deps",
"test": "npm run test --workspaces",
"unittest:vitest": "npm run unittest:vitest --workspaces",
"wip:unittest:vitest:coverage": "vitest run --coverage -r packages/xxx",
"build": "npm run build --workspaces",
"build:react": "npm run build:react --workspace=packages/editor",
"lint": "npm run lint --workspaces",
"watch": "npm run build && npm run --parallel watch",
"start": "npm run start-react",
"start-react": "npm run start --workspace=packages/editor",
"start:preview": "npm run preview --workspace=packages/editor",
Expand Down
8 changes: 5 additions & 3 deletions packages/editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
"private": true,
"dependencies": {
"react-confetti-explosion": "^2.1.2",
"@hocuspocus/provider": "^2.4.0",
"@hocuspocus/provider": "2.4.0",
"@atlaskit/atlassian-navigation": "^2.6.13",
"@atlaskit/avatar": "^21.3.7",
"@atlaskit/breadcrumbs": "^11.10.5",
"@atlaskit/button": "^16.8.2",
"@atlaskit/css-reset": "^6.5.3",
"@atlaskit/checkbox": "^13.3.0",
"@atlaskit/dropdown-menu": "^11.10.5",
"@atlaskit/empty-state": "^7.6.3",
"@atlaskit/flag": "^15.2.15",
Expand All @@ -26,7 +27,7 @@
"@atlaskit/textfield": "^5.6.3",
"@atlaskit/tree": "^8.8.5",
"@tiptap/core": "^2.0.4",
"@blocknote/core": "^0.9.3",
"@blocknote/core": "^0.13.2",
"@emotion/react": "^11.4.0",
"@supabase/auth-ui-react": "^0.4.5",
"@supabase/auth-ui-shared": "^0.1.7",
Expand Down Expand Up @@ -57,14 +58,15 @@
"vscode-lib": "^0.1.2",
"web-vitals": "^1.0.1",
"y-indexeddb": "9.0.6",
"y-websocket": "^2.0.3",
"y-protocols": "^1.0.5",
"yjs": "^13.6.4",
"react-inspector": "^6.0.1"
},
"scripts": {
"copytypes:self": "tsc --declaration --emitDeclarationOnly --noEmit false --composite false --declarationDir ./public/types/@typecell-org/editor",
"copytypes:externaldep": "mkdir -p public/types/$npm_config_pkgname && cp -rf ../../node_modules/$npm_config_pkgname/. public/types/$npm_config_pkgname",
"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",
"copytypes:allexternaldeps": "npm run copytypes:externaldep --pkgname=@types/react && npm run copytypes:externaldep --pkgname=@types/prop-types && npm run copytypes:externaldep --pkgname=csstype",
"copytypes:dep": "mkdir -p public/types/@typecell-org/$npm_config_pkgname && cp -rf ../$npm_config_pkgname/types/. public/types/@typecell-org/$npm_config_pkgname",
"copytypes:alldeps": "npm run copytypes:dep --pkgname=util && npm run copytypes:dep --pkgname=engine && npm run copytypes:dep --pkgname=frame",
"copytypes": "npm run copytypes:self && npm run copytypes:alldeps && npm run copytypes:allexternaldeps",
Expand Down
1 change: 1 addition & 0 deletions packages/editor/public/_docs/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"manual/3. Reactive variables.md",
"manual/4. Inputs.md",
"manual/5. Imports and NPM.md",
"manual/6. Plugins.md",
"README.md"
]
}
85 changes: 85 additions & 0 deletions packages/editor/public/_docs/manual/6. Plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Plugins

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.

## Map block

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.

Let's first set up the code to render a Map, based on _react-map-gl_.

### Map code

First, let's set up some reactive variables for our map component:

```typescript
export let zoom = 1;
export let latitude = 1;
export let longitude = 1;
export let markers: Array<{
latitude: number;
longitude: number;
color: string;
text: string;
}> = [];
```

And import the required CSS stylesheet:

```typescript
import css from "maplibre-gl/dist/maplibre-gl.css";
export { css };
```

Now, let's create the main code that renders our map component:

```typescript
import MapLibre, { Marker, Source, Layer, Popup } from "react-map-gl/maplibre";
import maplibregl from "maplibre-gl";

export const map = (
<div style={{ width: 700, height: 400 }}>
<MapLibre
onMove={(e) => {
$.zoom = e.viewState.zoom;
$.latitude = e.viewState.latitude;
$.longitude = e.viewState.longitude;
}}
longitude={$.longitude}
latitude={$.latitude}
zoom={$.zoom}
mapStyle="https://demotiles.maplibre.org/style.json">
{$.markers.map((m, i) => (
<Marker
key={i}
latitude={m.latitude}
longitude={m.longitude}
color={m.color}
popup={m.text ? new maplibregl.Popup().setText(m.text) : undefined}
/>
))}
</MapLibre>
</div>
);
```

### Register a plugin

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.

```typescript
// Plugin registration
typecell.editor.registerBlock({
name: "Map",
blockVariable: "map",
// Variables for properties screen that's auto-generated
// and shows when clicking the settings-gear icon
settings: {
latitude: true,
longitude: true,
zoom: true,
},
});
```

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.
37 changes: 24 additions & 13 deletions packages/editor/src/app/documentRenderers/richtext/FrameHost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,43 +65,54 @@ export function FrameHost(props: {
>();

const methods = {
markPlugins: async (identifierStr: string, value: boolean) => {
const identifier = parseIdentifier(identifierStr);
props.sessionStore.documentCoordinator?.markPlugins(identifier, value);
},
processYjsMessage: async (message: ArrayBuffer) => {
provider.onMessage(message, "penpal");
},
registerTypeCellModuleCompiler: async (moduleName: string) => {
if (moduleManagers.has(moduleName)) {
console.warn("already has moduleManager for", moduleName);
return;
}
resolveModuleName: async (moduleName: string) => {
if (!moduleName.startsWith("!")) {
throw new Error("invalid module name");
}
const identifierStr = moduleName.substring(1);

const identifier = parseIdentifier(moduleName.substring(1));
const identifierStr = identifier.toString();
return identifierStr;
},
registerTypeCellModuleCompiler: async (identifierStr: string) => {
const identifier = parseIdentifier(identifierStr);
if (moduleManagers.has(identifierStr)) {
console.warn("already has moduleManager for", identifierStr);
return identifierStr;
}

const provider = new DocumentResourceModelProvider(
identifier,
props.sessionStore,
);

const forwarder = new ModelForwarder(
"modules/" + moduleName,
"modules/" + identifierStr,
provider,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
connectionMethods.current!,
);
moduleManagers.set(moduleName, { provider, forwarder });

moduleManagers.set(identifierStr, { provider, forwarder });
await forwarder.initialize();
return identifier.toString();
return identifierStr;
},
unregisterTypeCellModuleCompiler: async (moduleName: string) => {
const moduleManager = moduleManagers.get(moduleName);
unregisterTypeCellModuleCompiler: async (identifierStr: string) => {
const moduleManager = moduleManagers.get(identifierStr);
if (!moduleManager) {
console.warn("no moduleManager for", moduleName);
console.warn("no moduleManager for", identifierStr);
return;
}
moduleManager.provider.dispose();
moduleManager.forwarder.dispose();
moduleManagers.delete(moduleName);
moduleManagers.delete(identifierStr);
},
};

Expand Down
6 changes: 3 additions & 3 deletions packages/editor/src/app/main/components/ProfilePopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import { toProfilePage } from "../../routes/routes";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const Trigger = observer((props: any) => {
const { triggerRef, isSelected, testId, ...passProps } = props;
const { triggerRef, isSelected, testId, sessionStore, ...passProps } = props;
return (
<Profile
testId="profile-button"
icon={
<Avatar
name={props.sessionStore.loggedInUserId}
src={props.sessionStore.profile?.avatar_url || undefined}
name={sessionStore.loggedInUserId}
src={sessionStore.profile?.avatar_url || undefined}
size="32"
round={true}
textSizeRatio={2}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ import { DocumentResource } from "../../../../store/DocumentResource";
import { SessionStore } from "../../../../store/local/SessionStore";
import {
ClosePermissionsDialog,
ClosePluginDialog,
IsPermissionsDialogOpen,
IsPluginDialogOpen,
OpenPermissionsDialog,
OpenPluginDialog,
} from "../../../routes/routes";
import { MenuBar } from "../menuBar/MenuBar";

Expand All @@ -26,6 +29,7 @@ import SupabasePermissionsDialog from "../../../supabase-auth/routes/permissions
import { Breadcrumb } from "./Breadcrumb";
import styles from "./DocumentMenu.module.css";
import { ForkAlert } from "./ForkAlert";
import PluginDialog from "./PluginDialog";
import { ShareButton } from "./ShareButton";

type Props = {
Expand All @@ -36,7 +40,7 @@ type Props = {
// TODO: move?
function userCanEditPermissions(
sessionStore: SessionStore,
identifier: Identifier
identifier: Identifier,
) {
if (identifier instanceof HttpsIdentifier) {
return false;
Expand All @@ -56,7 +60,7 @@ export const DocumentMenu: React.FC<Props> = observer((props) => {
const { sessionStore } = props;
const canEditPermissions = userCanEditPermissions(
sessionStore,
props.document.identifier
props.document.identifier,
);
const location = useLocation();
const navigate = useNavigate();
Expand Down Expand Up @@ -127,13 +131,27 @@ export const DocumentMenu: React.FC<Props> = observer((props) => {
Permissions
</DropdownItem>
)}
{props.document instanceof DocumentResource && (
<DropdownItem onClick={() => OpenPluginDialog(navigate)}>
Plugins
</DropdownItem>
)}
</DropdownMenu>
</li>
</>
)}
</ul>
</aside>
{canEditPermissions && permissionsArea}
{props.document instanceof DocumentResource && (
<PluginDialog
close={() => ClosePluginDialog(navigate)}
isOpen={IsPluginDialogOpen(location)}
identifier={props.document.identifier}
sessionStore={sessionStore}
document={props.document}
/>
)}
</MenuBar>
);
});
Loading

0 comments on commit c80b9f3

Please sign in to comment.