, onfulfilled: (value: T) => (U | PromiseLike)): EngraftPromise {
7 | const later = hookLater();
8 | return promise.then((value) =>
9 | later(() =>
10 | onfulfilled(value)
11 | )
12 | );
13 | };
14 |
--------------------------------------------------------------------------------
/packages/core/src/ToolResultWithScope.ts:
--------------------------------------------------------------------------------
1 | import { hookMemo, hooks } from "@engraft/refunc";
2 | import { ToolProgram, ToolProps, ToolResult, VarBindings } from "./core.js";
3 | import { hookRunTool } from "./runTool.js";
4 |
5 | // This is a utility for keeping track of a tool that is run with newly provided
6 | // var bindings, so the new var bindings can also be provided when the tool's
7 | // view is rendered.
8 |
9 | // It's not great, but it's helped so far.
10 |
11 | export type ToolResultWithScope = {
12 | result: ToolResult
,
13 | newScopeVarBindings: VarBindings,
14 | }
15 |
16 | export function hookRunToolWithNewVarBindings
(
17 | props: Omit, 'varBindings'> & { varBindings?: VarBindings, newVarBindings: VarBindings }
18 | ): ToolResultWithScope {
19 | const allVarBindings = hookMemo(() => ({
20 | ...props.varBindings,
21 | ...props.newVarBindings,
22 | }), [props.varBindings, props.newVarBindings]);
23 |
24 | const result = hookRunTool({...props, varBindings: allVarBindings});
25 |
26 | return { result, newScopeVarBindings: props.newVarBindings };
27 | }
28 |
29 | export const runToolWithNewVarBindings = hooks(hookRunToolWithNewVarBindings);
30 |
--------------------------------------------------------------------------------
/packages/core/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./context.js";
2 | export * from "./core.js";
3 | export * from "./EngraftPromise-refunc.js";
4 | export * from "./EngraftPromise.js";
5 | export * from "./randomId.js";
6 | export * from "./runTool.js";
7 | export * from "./test-utils.js";
8 | export * from "./toolFromModule.js";
9 | export * from "./ToolResultWithScope.js"
10 |
--------------------------------------------------------------------------------
/packages/core/src/randomId.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'friendly-words' {
2 | const objects: string[];
3 | }
4 |
--------------------------------------------------------------------------------
/packages/core/src/randomId.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import { objects } from "friendly-words";
4 |
5 | export function randomId(random: () => number = Math.random): string {
6 | return `ID${objects[Math.floor(random() * objects.length)]}${random().toFixed(6).slice(2)}`;
7 | }
8 |
--------------------------------------------------------------------------------
/packages/core/src/runTool.tsx:
--------------------------------------------------------------------------------
1 | import { hookDedupe, hookFork, hookMemo, hookRefunction, hooks } from "@engraft/refunc";
2 | import { objEqWithRefEq } from "@engraft/shared/lib/eq.js";
3 | import { ToolProgram, ToolProps, ToolResult, VarBindings } from "./core.js";
4 |
5 | export function hookRunTool
(
6 | props: ToolProps
7 | ): ToolResult
{
8 | const toolName = props.program.toolName;
9 | const tool = props.context.dispatcher.lookUpToolByProgram(props.program);
10 |
11 | // A tool only receives the varBindings that it references
12 | const varBindings = hookRelevantVarBindings(props);
13 |
14 | if (props.context.debugMode) { console.group(`running ${toolName}`); }
15 | try {
16 | // Run in a branch keyed by the toolName, so that a new memory is used if toolName changes
17 | return hookFork((branch) => branch(toolName, () =>
18 | hookRefunction(tool.run, {...props, varBindings})
19 | ));
20 | } finally {
21 | if (props.context.debugMode) { console.groupEnd(); }
22 | }
23 | }
24 |
25 | export const runTool = hooks(hookRunTool);
26 |
27 | function hookRelevantVarBindings(props: ToolProps) {
28 | const refs = props.context.dispatcher.referencesForProgram(props.program); // cached in a weak-map so no memo
29 | const relevantVarBindings = hookMemo(() => {
30 | const result: VarBindings = {};
31 | for (const ref of refs) {
32 | result[ref] = props.varBindings[ref];
33 | }
34 | return hookDedupe(result, objEqWithRefEq);
35 | }, [refs, props.varBindings]);
36 |
37 | return relevantVarBindings;
38 | }
39 |
--------------------------------------------------------------------------------
/packages/core/src/test-utils.ts:
--------------------------------------------------------------------------------
1 | import { ToolOutput, VarBindings } from "./index.js";
2 | import { EngraftPromise } from "./EngraftPromise.js";
3 |
4 | export function makeVarBindings(values: {[name: string]: ToolOutput | EngraftPromise}): VarBindings {
5 | const bindings: VarBindings = {};
6 | for (const name of Object.keys(values)) {
7 | bindings[name] = {
8 | var_: {id: name, label: name},
9 | outputP: EngraftPromise.resolve(values[name]),
10 | };
11 | }
12 | return bindings;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/core/src/toolFromModule.ts:
--------------------------------------------------------------------------------
1 | import { Tool, ToolProgram } from "./index.js";
2 | import { hasProperty } from "@engraft/shared/lib/hasProperty.js";
3 |
4 | export type ToolModule = Tool
| { default: Tool
} | { tool: Tool
};
5 |
6 | export function toolFromModule
(module: ToolModule
): Tool
{
7 | if (hasProperty(module, "tool")) {
8 | return module.tool;
9 | } else if (hasProperty(module, "default")) {
10 | return module.default;
11 | } else {
12 | return module;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/core/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/eslint-plugin-refunc-hooks/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/eslint-plugin-refunc-hooks",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "ESLint rules for Refunc Hooks (adapted from eslint-plugin-react-hooks)",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "test:watch": "vitest",
11 | "test": "vitest run",
12 | "depcheck": "node ../../scripts/our-depcheck.cjs",
13 | "lint": "eslint --max-warnings=0 ."
14 | },
15 | "main": "src/index.js",
16 | "peerDependencies": {
17 | "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0"
18 | },
19 | "devDependencies": {
20 | "@types/eslint": "^8.21.1",
21 | "@typescript-eslint/utils": "^5.54.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/packages/eslint-plugin-refunc-hooks/src/index.js:
--------------------------------------------------------------------------------
1 | const ExhaustiveDeps = require('./ExhaustiveDeps');
2 |
3 | module.exports = {
4 | configs: {
5 | recommended: {
6 | plugins: ['@engraft/refunc-hooks'],
7 | rules: {
8 | '@engraft/refunc-hooks/exhaustive-deps': 'warn',
9 | },
10 | },
11 | },
12 |
13 | rules: {
14 | 'exhaustive-deps': ExhaustiveDeps,
15 | },
16 | };
17 |
--------------------------------------------------------------------------------
/packages/extension-observable/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/extension-observable",
3 | "private": true,
4 | "version": "0.0.9",
5 | "type": "module",
6 | "scripts": {
7 | "build": "vite build",
8 | "depcheck": "node ../../scripts/our-depcheck.cjs"
9 | },
10 | "devDependencies": {
11 | "@types/codemirror": "^5.60.7",
12 | "@types/escodegen": "^0.0.7",
13 | "@types/esprima": "^4.0.3",
14 | "@types/estraverse": "^5.1.2",
15 | "vite": "^4.3.2",
16 | "vitest": "^0.31.4"
17 | },
18 | "dependencies": {
19 | "@codemirror/view": "^6.13.0",
20 | "@engraft/core": "^0.0.9",
21 | "@types/estree": "^1.0.1",
22 | "escodegen": "^2.0.0",
23 | "esprima": "^4.0.1",
24 | "estraverse": "^5.3.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/extension-observable/public/content_script.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | console.log("Observable Writer: content_script.js running")
4 |
5 | const scriptElem = document.createElement('script');
6 | scriptElem.src = chrome.runtime.getURL('injected_script.js');
7 | scriptElem.onload = function() {
8 | this.remove();
9 | };
10 | (document.head || document.documentElement).appendChild(scriptElem);
11 |
--------------------------------------------------------------------------------
/packages/extension-observable/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Engraft-Observable Writer",
3 | "description": "Write to Observable cells from their output",
4 | "version": "1.0",
5 | "manifest_version": 3,
6 | "permissions": ["activeTab", "tabs", "scripting"],
7 | "host_permissions": [
8 | "https://observablehq.com/*"
9 | ],
10 | "content_scripts": [
11 | {
12 | "matches": ["https://observablehq.com/*"],
13 | "js": ["content_script.js"]
14 | }
15 | ],
16 | "web_accessible_resources": [{
17 | "resources": ["injected_script.js"],
18 | "matches": [""]
19 | }]
20 | }
21 |
--------------------------------------------------------------------------------
/packages/extension-observable/src/util/version.ts:
--------------------------------------------------------------------------------
1 | const version = 2.0
2 | // TODO: report package version from manifest.json
3 | // This is accessed at runtime, so it is pending the cross-browser refactor
4 | export default version;
--------------------------------------------------------------------------------
/packages/extension-observable/vite.config.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { defineConfig } from 'vite'
3 | import {resolve} from "path";
4 |
5 | // TODO: how to keep this config in sync with repo-wide vite.config.ts?
6 | export default defineConfig({
7 | test: {
8 | },
9 | build: {
10 | lib: {
11 | entry: resolve(__dirname, 'src/injected_script.ts'),
12 | name: 'injected_script',
13 | fileName: 'injected_script',
14 | },
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/packages/fancy-setup/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/fancy-setup",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Maximalist setup for Engraft, including some large / troublesome components",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "test:watch": "vitest",
11 | "test": "vitest run",
12 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/basic-setup": "^0.0.9",
21 | "@engraft/core": "^0.0.9",
22 | "@engraft/tool-example-datasets": "^0.0.9",
23 | "@engraft/tool-geom": "^0.0.9",
24 | "@engraft/tool-python": "^0.0.9",
25 | "@engraft/tool-voyager": "^0.0.9"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/fancy-setup/src/index.ts:
--------------------------------------------------------------------------------
1 | import { makeBasicContext } from "@engraft/basic-setup";
2 | import { Tool, forgetP, toolFromModule } from "@engraft/core";
3 | import ExampleDatasets from "@engraft/tool-example-datasets";
4 | import Geom from "@engraft/tool-geom";
5 | import Python from "@engraft/tool-python";
6 | import Voyager from "@engraft/tool-voyager";
7 |
8 | const tools: Tool[] = [
9 | forgetP(toolFromModule(ExampleDatasets)),
10 | forgetP(toolFromModule(Geom)),
11 | forgetP(toolFromModule(Python)),
12 | forgetP(toolFromModule(Voyager)),
13 | ];
14 |
15 | export function makeFancyContext() {
16 | const context = makeBasicContext();
17 |
18 | for (const tool of tools) {
19 | context.dispatcher.registerTool(tool);
20 | }
21 |
22 | return context
23 | }
24 |
--------------------------------------------------------------------------------
/packages/fancy-setup/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/fancy-setup/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/graft-garden/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Graft Garden
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/packages/graft-garden/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@engraft/graft-garden",
4 | "version": "0.0.9",
5 | "license": "MIT",
6 | "description": "DESCRIPTION TODO",
7 | "scripts": {
8 | "depcheck": "node ../../scripts/our-depcheck.cjs",
9 | "lint": "eslint --max-warnings=0 .",
10 | "tsc": "tsc",
11 | "build-app": "vite build --config ../../vite.config.ts",
12 | "preview": "vite preview --config ../../vite.config.ts",
13 | "dev": "vite --config ../../vite.config.ts"
14 | },
15 | "type": "module",
16 | "dependencies": {
17 | "@engraft/fancy-setup": "^0.0.9",
18 | "@engraft/hostkit": "^0.0.9",
19 | "@engraft/shared": "^0.0.9",
20 | "@engraft/tool-vite-lib": "^0.0.9",
21 | "@popperjs/core": "^2.11.7",
22 | "bootstrap": "^5.2.0",
23 | "firebase": "^9.9.1",
24 | "firebaseui": "^6.0.1",
25 | "lodash": "^4.17.21",
26 | "react": "^18.0.0",
27 | "react-dom": "^18.0.0",
28 | "react-error-boundary": "^3.1.4",
29 | "react-firebase-hooks": "^5.0.3",
30 | "react-router-dom": "^6.3.0"
31 | },
32 | "devDependencies": {
33 | "vite": "^4.3.2"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/graft-garden/src/ViewPatch.tsx:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import { ToolOutputView, runTool, useRefunction } from "@engraft/hostkit";
4 | import bootstrapCss from "bootstrap/dist/css/bootstrap.min.css?inline";
5 | import { doc } from "firebase/firestore";
6 | import { memo, useEffect } from "react";
7 | import { useDocumentData } from "react-firebase-hooks/firestore";
8 | import { useParams } from "react-router-dom";
9 | import { Patch, patchesRef } from "./db.js";
10 | import { usePatchState } from "./usePatchState.js";
11 | import { context } from "./util.js";
12 |
13 |
14 | export const ViewPatch = memo(function ViewPatch() {
15 | const params = useParams();
16 | const patchId = params.patchId;
17 |
18 | const [patch, _loading, error, _snapshot] = useDocumentData(doc(patchesRef, patchId));
19 |
20 | useEffect(() => {
21 | document.title = `${patch?.name || 'no name'}`;
22 | }, [patch?.name]);
23 |
24 | return <>
25 |
26 |
27 | { patch
28 | ?
29 | : error
30 | ? error: {error.message}
31 | : loading...
32 | }
33 |
34 | >
35 | });
36 |
37 | type ViewPatchActualViewProps = {
38 | patch: Patch,
39 | };
40 |
41 | const ViewPatchActualView = memo(function ViewPatchActualView(props: ViewPatchActualViewProps) {
42 | const { patch } = props;
43 | const { varBindings } = usePatchState(patch);
44 | const { outputP } = useRefunction(runTool, { program: patch.toolProgram, varBindings, context });
45 |
46 | return ;
47 | })
48 |
--------------------------------------------------------------------------------
/packages/graft-garden/src/react-firebase-hooks.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'react-firebase-hooks/firestore' {
2 | export * from 'react-firebase-hooks/firestore/dist/firestore/index.js';
3 | }
4 |
--------------------------------------------------------------------------------
/packages/graft-garden/src/usePatchState.tsx:
--------------------------------------------------------------------------------
1 | import { EngraftPromise, VarBindings, up } from "@engraft/hostkit";
2 | import { useMemo, useState } from "react";
3 | import { Patch } from "./db.js";
4 |
5 | export function usePatchState(patch: Patch) {
6 | const [state, setState] = useState(() => patch.initialStateJSON && JSON.parse(patch.initialStateJSON));
7 | const stateUP = up(setState);
8 |
9 | const varBindings: VarBindings = useMemo(() => {
10 | if (patch.initialStateJSON === undefined) {
11 | const varBindings: VarBindings = {};
12 | return varBindings;
13 | } else {
14 | const stateVarId = 'IDstate000000';
15 | const stateUPVarId = 'IDstateUP000000';
16 |
17 | return {
18 | [stateVarId]: {
19 | var_: {id: stateVarId, label: 'state'},
20 | outputP: EngraftPromise.resolve({ value: state })
21 | },
22 | [stateUPVarId]: {
23 | var_: {id: stateUPVarId, label: 'stateUP'},
24 | outputP: EngraftPromise.resolve({ value: stateUP })
25 | },
26 | };
27 | }
28 | }, [patch.initialStateJSON, state, stateUP]);
29 |
30 | return { state, stateUP, varBindings }
31 | }
32 |
--------------------------------------------------------------------------------
/packages/graft-garden/src/util.tsx:
--------------------------------------------------------------------------------
1 | import { makeFancyContext } from "@engraft/fancy-setup";
2 | import { toolFromModule } from "@engraft/hostkit";
3 | import { getAuth, onAuthStateChanged, User } from "firebase/auth";
4 | import { useEffect, useState } from "react";
5 | import ViteLib from "@engraft/tool-vite-lib";
6 |
7 | export function useUser() {
8 | const [user, setUser] = useState(null);
9 | useEffect(() => {
10 | const unsubscribe = onAuthStateChanged(getAuth(), (user) => {
11 | setUser(user);
12 | });
13 | return () => unsubscribe();
14 | }, []);
15 | return user;
16 | }
17 |
18 | // TODO: it's a hack to put this here as a global
19 | export const context = makeFancyContext();
20 |
21 | // TODO: snuck this in cuz it's nice
22 | context.dispatcher.registerTool(toolFromModule(ViteLib))
23 |
--------------------------------------------------------------------------------
/packages/graft-garden/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "types": [
6 | "vite/client"
7 | ]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/host-observable/README.md:
--------------------------------------------------------------------------------
1 | # Engraft-Observable Embedding (Host)
2 |
3 | This directory contains the codebase for the host that runs Engraft within [Observable](https://observablehq.com)
4 |
5 |
6 | [User Guide](https://observablehq.com/@wongyuhao/engraft-embed-docs)
7 |
8 | [Developer Doc](https://observablehq.com/@wongyuhao/engraft-embed)
9 |
10 |
11 | The host is a React component that wraps hooks necessary to run Engraft.
12 | On the notebook side, there is another React component that wraps this embedding for additional logic (see dev doc).
13 |
14 | By convention, code should be written as close to Observable as possible (i.e. in the notebook), and only in this repo when necessary. (e.g. when needing strictly typed code, etc.)
15 |
16 | This codebase is meant to be bundled into a .js lib with Vite, and imported into the [Observable host notebook](https://observablehq.com/@wongyuhao/engraft-embed).
17 |
18 | ## Development notes
19 |
20 | This package is currently an "app" rather than a "lib" – it would be nice if it could be both, but that causes issues at least with CSS handling...
21 |
--------------------------------------------------------------------------------
/packages/host-observable/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/host-observable",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "A library for embedding Engraft in Observable",
6 | "type": "module",
7 | "publishConfig": {
8 | "access": "public"
9 | },
10 | "scripts": {
11 | "depcheck": "node ../../scripts/our-depcheck.cjs",
12 | "lint": "eslint --max-warnings=0 .",
13 | "tsc": "tsc",
14 | "build-app": "vite build --config ./vite.config.ts"
15 | },
16 | "main": "lib/index.js",
17 | "dependencies": {
18 | "@engraft/core": "^0.0.9",
19 | "@engraft/fancy-setup": "^0.0.9",
20 | "@engraft/hostkit": "^0.0.9",
21 | "@engraft/shared": "^0.0.9",
22 | "@observablehq/inspector": "^5.0.0",
23 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc",
24 | "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
25 | },
26 | "devDependencies": {
27 | "vite": "^4.3.2"
28 | },
29 | "peerDependencies": {
30 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc",
31 | "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/packages/host-observable/src/ObservableInspector.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@observablehq/inspector' {
2 | export class Inspector {
3 | constructor(elem: HTMLElement)
4 | fulfilled(value: any): void
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/host-observable/src/ObservableInspector.tsx:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import { Inspector } from "@observablehq/inspector";
4 | import { memo, useEffect, useState } from "react";
5 |
6 | import css from "./ObservableInspector.css?inline";
7 |
8 | export type ObservableInspectorProps = {
9 | value: any,
10 | }
11 |
12 | export const ObservableInspector = memo(({value}: ObservableInspectorProps) => {
13 | const [elem, setElem] = useState(null);
14 | const [inspector, setInspector] = useState(null);
15 |
16 | useEffect(() => {
17 | setInspector(elem && new Inspector(elem))
18 | }, [elem]);
19 |
20 | useEffect(() => {
21 | if (inspector) {
22 | inspector.fulfilled(value);
23 | }
24 | }, [inspector, value])
25 |
26 | return <>
27 |
28 |
29 | >;
30 | });
31 |
--------------------------------------------------------------------------------
/packages/host-observable/src/index.css:
--------------------------------------------------------------------------------
1 | .bannerContainer {
2 | font-family: monospace;
3 | font-size: 0.7em;
4 | display: flex;
5 | justify-content: space-between;
6 | margin-top: 2em;
7 | flex-direction: column;
8 | }
9 |
10 | .ext-inactive {
11 | padding: 1em 1em;
12 | background-color: rgba(255, 0, 0, 0.05);
13 | color: rgba(255, 0, 0, 0.8);
14 | display: flex;
15 | flex-direction: row;
16 | justify-content: space-between;
17 | align-items: center;
18 | }
19 |
20 | .ext-active {
21 | padding: 1em 1em;
22 | background-color: #f5f5f5;
23 | color: rgba(0, 0, 0, 0.5);
24 | display: flex;
25 | flex-direction: row;
26 | justify-content: space-between;
27 | align-items: center;
28 | }
29 |
30 | .bannerToolbar {
31 | padding: 0.5em 1em;
32 | background-color: #f5f5f5;
33 | color: rgba(0, 0, 0, 1);
34 | display: flex;
35 | flex-direction: row;
36 | justify-content: space-between;
37 | align-items: center;
38 | gap: 1em
39 | }
40 |
--------------------------------------------------------------------------------
/packages/host-observable/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "types": [
6 | "vite/client"
7 | ]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/host-observable/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { resolve } from 'path';
2 | import { defineConfig } from "vite";
3 |
4 | export default defineConfig({
5 | resolve: {
6 | alias: {
7 | // Not sure how `chalk` is getting into builds, but it breaks things. This works!
8 | 'chalk': '/dev/null',
9 | }
10 | },
11 | define: {
12 | 'process.env.BABEL_TYPES_8_BREAKING': false,
13 | 'process.env.NODE_ENV': JSON.stringify('production'),
14 | 'process.env': {},
15 | 'process.emitWarning': false,
16 | },
17 | build: {
18 | lib: {
19 | entry: resolve(__dirname, 'src/index.tsx'),
20 | name: 'EngraftObservableHost',
21 | fileName: 'engraft-observable-host',
22 | },
23 | // TODO: these are copied from the root vite.config.js; idk the best way to share them
24 | commonjsOptions: { },
25 | rollupOptions: {
26 | external: [
27 | 'pyodide/pyodide.js',
28 | ],
29 | },
30 | },
31 | });
32 |
--------------------------------------------------------------------------------
/packages/host-python/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 |
--------------------------------------------------------------------------------
/packages/host-python/engraft.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | import json
3 | import numpy as np
4 |
5 | class _CustomEncoder(json.JSONEncoder):
6 | def default(self, obj):
7 | if isinstance(obj, np.ndarray):
8 | return {"__type": "nd-array", "__value": obj.tolist()}
9 | return json.JSONEncoder.default(self, obj)
10 |
11 | class _CustomDecoder(json.JSONDecoder):
12 | def decode(self, json_string):
13 | def object_hook(obj):
14 | if '__type' in obj:
15 | if obj['__type'] == 'nd-array':
16 | return np.array(obj['__value'])
17 | return obj
18 | return json.loads(json_string, object_hook=object_hook)
19 |
20 | def run_engraft(data, program, *, edit):
21 | if hasattr(data, "json"):
22 | data = data.json()
23 |
24 | data = json.dumps(data, cls=_CustomEncoder)
25 |
26 | command = ["engraft", program, "--json-only"]
27 |
28 | if edit:
29 | command.append("--edit")
30 |
31 | output = subprocess.check_output(command, input=data.encode())
32 | output = _CustomDecoder().decode(output)
33 | return output
34 |
--------------------------------------------------------------------------------
/packages/host-python/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@engraft/host-python",
4 | "version": "0.0.9",
5 | "description": "Python embedding of Engraft",
6 | "type": "module",
7 | "scripts": {
8 | "test": "cd tests; python3 test_basic.py"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/host-python/tests/mixed_type.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolName": "slot",
3 | "modeName": "tool",
4 | "subProgram": {
5 | "toolName": "notebook",
6 | "cells": [
7 | {
8 | "var_": {
9 | "id": "IDspice017082",
10 | "label": "A"
11 | },
12 | "program": {
13 | "toolName": "slot",
14 | "modeName": "code",
15 | "code": "IDinput000000[2]",
16 | "defaultCode": "IDinput000000",
17 | "subPrograms": {}
18 | }
19 | }
20 | ],
21 | "prevVarId": "IDend151100"
22 | }
23 | }
--------------------------------------------------------------------------------
/packages/host-python/tests/nd_array.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolName": "slot",
3 | "modeName": "tool",
4 | "subProgram": {
5 | "toolName": "notebook",
6 | "cells": [
7 | {
8 | "var_": {
9 | "id": "IDwallet206970",
10 | "label": "A"
11 | },
12 | "program": {
13 | "toolName": "slot",
14 | "modeName": "tool",
15 | "subProgram": {
16 | "toolName": "python",
17 | "code": "IDinput000000 + 4",
18 | "subPrograms": {}
19 | },
20 | "defaultCode": "IDinput000000"
21 | }
22 | }
23 | ],
24 | "prevVarId": "IDbag909751"
25 | }
26 | }
--------------------------------------------------------------------------------
/packages/host-python/tests/nd_array_multidimensional.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolName": "slot",
3 | "modeName": "tool",
4 | "subProgram": {
5 | "toolName": "notebook",
6 | "cells": [
7 | {
8 | "var_": {
9 | "id": "IDmoustache918852",
10 | "label": "A"
11 | },
12 | "program": {
13 | "toolName": "slot",
14 | "modeName": "tool",
15 | "subProgram": {
16 | "toolName": "python",
17 | "code": "import numpy as np\nnp.dot(IDinput000000, np.array([1, 2, 3]))",
18 | "subPrograms": {}
19 | },
20 | "defaultCode": "IDinput000000"
21 | }
22 | }
23 | ],
24 | "prevVarId": "IDtendency543356"
25 | }
26 | }
--------------------------------------------------------------------------------
/packages/host-python/tests/wikipedia.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolName": "slot",
3 | "modeName": "tool",
4 | "subProgram": {
5 | "toolName": "notebook",
6 | "cells": [
7 | {
8 | "var_": {
9 | "id": "IDclownfish554839",
10 | "label": "A"
11 | },
12 | "program": {
13 | "toolName": "slot",
14 | "modeName": "tool",
15 | "subProgram": {
16 | "toolName": "extractor",
17 | "inputProgram": {
18 | "toolName": "slot",
19 | "modeName": "code",
20 | "code": "IDinput000000",
21 | "defaultCode": "IDinput000000",
22 | "subPrograms": {}
23 | },
24 | "patternsWithIds": [
25 | {
26 | "id": "IDshawl576793",
27 | "pattern": [
28 | "query",
29 | "pages",
30 | {
31 | "wildcard": true
32 | },
33 | "title"
34 | ]
35 | }
36 | ],
37 | "minimized": false
38 | },
39 | "defaultCode": "IDinput000000"
40 | }
41 | },
42 | {
43 | "var_": {
44 | "id": "IDtank071399",
45 | "label": "B"
46 | },
47 | "program": {
48 | "toolName": "slot",
49 | "modeName": "code",
50 | "code": "[IDturner242133[1], IDclownfish554839[2]]",
51 | "defaultCode": "IDturner242133",
52 | "subPrograms": {}
53 | }
54 | }
55 | ],
56 | "prevVarId": "IDturner242133"
57 | }
58 | }
--------------------------------------------------------------------------------
/packages/hostkit/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/hostkit",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "A bundle of libraries needful or helpful for defining Engraft hosts",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/core": "^0.0.9",
21 | "@engraft/core-widgets": "^0.0.9",
22 | "@engraft/react": "^0.0.9",
23 | "@engraft/refunc-react": "^0.0.9",
24 | "@engraft/shared": "^0.0.9",
25 | "@engraft/update-proxy": "^0.0.9"
26 | },
27 | "peerDependencies": {
28 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/hostkit/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "@engraft/core";
2 | export * from "@engraft/react";
3 | export * from "@engraft/core-widgets";
4 | export * from "@engraft/refunc-react";
5 | export * from "@engraft/update-proxy";
6 | export * from "./ToolWithView.js";
7 |
--------------------------------------------------------------------------------
/packages/hostkit/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/hostkit/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/original-tools/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/original-tools",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Temporary package to port over old tools into the monorepo.",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/hostkit": "^0.0.9",
21 | "@engraft/shared": "^0.0.9",
22 | "@engraft/toolkit": "^0.0.9",
23 | "d3-dsv": "^3.0.1",
24 | "lodash": "^4.17.21",
25 | "react": "^18.0.0",
26 | "react-colorful": "^5.5.1",
27 | "react-dom": "^18.2.0",
28 | "react-dropzone": "^12.0.4",
29 | "react-select": "^5.7.0 <5.7.1",
30 | "react-vega": "^7.6.0",
31 | "seedrandom": "^3.0.5",
32 | "vega": "^5.22.1",
33 | "vega-lite": "^5.6.0"
34 | },
35 | "devDependencies": {
36 | "@types/d3-dsv": "^3.0.1",
37 | "@types/google.maps": "^3.52.0",
38 | "@types/lodash": "^4.14.178",
39 | "@types/object-inspect": "^1.8.1",
40 | "@types/react": "^18.0.5",
41 | "@types/react-dom": "^18.0.0",
42 | "@types/react-inspector": "^4.0.2",
43 | "@types/react-test-renderer": "^18.0.0",
44 | "@types/seedrandom": "^3.0.2"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/original-tools/src/color/TODO.md:
--------------------------------------------------------------------------------
1 | [ ] drag causes vibration
2 |
--------------------------------------------------------------------------------
/packages/original-tools/src/color/index.tsx:
--------------------------------------------------------------------------------
1 | import { defineSimpleTool } from "@engraft/toolkit";
2 | import { RgbColor, RgbColorPicker } from "react-colorful";
3 |
4 | export default defineSimpleTool({
5 | name: 'color',
6 | fields: {
7 | r: 250,
8 | g: 200,
9 | b: 100,
10 | },
11 | subTools: [],
12 | compute: ({ fields }) => {
13 | return `rgb(${fields.r}, ${fields.g}, ${fields.b})`;
14 | },
15 | render: ({ fields, fieldsUP }) => {
16 | const onChange = (color: RgbColor) => {
17 | fieldsUP.$helper({$merge: color});
18 | };
19 | return ;
24 | },
25 | });
26 |
--------------------------------------------------------------------------------
/packages/original-tools/src/formatter/builtin.css:
--------------------------------------------------------------------------------
1 | .box {
2 | border: 1px solid gray;
3 | padding: 10px;
4 | margin: 2px;
5 | }
6 |
7 | .card {
8 | margin: 16px;
9 | background: white;
10 | border-radius: 6px;
11 | padding: 12px;
12 | box-shadow: 0 1px 4px rgba(20,20,20,0.2);
13 | }
14 |
--------------------------------------------------------------------------------
/packages/original-tools/src/index.ts:
--------------------------------------------------------------------------------
1 | import { forgetP, Tool, toolFromModule } from "@engraft/toolkit";
2 | import * as Color from "./color/index.js";
3 | import * as File from "./file/index.js";
4 | import * as Formatter from "./formatter/index.js";
5 | import * as NPM from "./npm/index.js";
6 | import * as NotFound from "./not-found/index.js";
7 | import * as Request from "./request/index.js";
8 | import * as SimpleChart from "./simple-chart/index.js";
9 | import * as Simulation from "./simulation/index.js";
10 | import * as Slider from "./slider/index.js";
11 | import * as TestArray from "./test-array/index.js";
12 | import * as TestDelay from "./test-delay/index.js";
13 | import * as TestSeeingDouble from "./test-seeing-double/index.js";
14 | import * as TestShowProgram from "./test-show-program/index.js";
15 | import * as TestTextLatency from "./test-text-latency/index.js";
16 |
17 | export const originalTools: Tool[] = [
18 | forgetP(toolFromModule(Color)),
19 | forgetP(toolFromModule(File)),
20 | forgetP(toolFromModule(Formatter)),
21 | forgetP(toolFromModule(NPM)),
22 | forgetP(toolFromModule(NotFound)),
23 | forgetP(toolFromModule(Request)),
24 | forgetP(toolFromModule(SimpleChart)),
25 | forgetP(toolFromModule(Simulation)),
26 | forgetP(toolFromModule(Slider)),
27 | forgetP(toolFromModule(TestArray)),
28 | forgetP(toolFromModule(TestDelay)),
29 | forgetP(toolFromModule(TestSeeingDouble)),
30 | forgetP(toolFromModule(TestShowProgram)),
31 | forgetP(toolFromModule(TestTextLatency)),
32 | ];
33 |
--------------------------------------------------------------------------------
/packages/original-tools/src/not-found/index.tsx:
--------------------------------------------------------------------------------
1 | import { CollectReferences, EngraftPromise, MakeProgram, ToolOutput, ToolProps, ToolView, defineTool, hookMemo, hooks, memoizeProps, renderWithReact } from "@engraft/toolkit";
2 |
3 | type Program = {
4 | toolName: 'not-found',
5 | }
6 |
7 | const makeProgram: MakeProgram = () => ({
8 | toolName: 'not-found',
9 | });
10 |
11 | const collectReferences: CollectReferences = (_program) => [];
12 |
13 | const run = memoizeProps(hooks((props: ToolProps) => {
14 | const { toolName } = props.program;
15 | const message = `ToolNotFoundError: ${toolName}`;
16 |
17 | const outputP = hookMemo(() => EngraftPromise.reject(
18 | new Error(message)
19 | ), [message]);
20 |
21 | const view: ToolView = hookMemo(() => ({
22 | render: renderWithReact(() => {message}
),
23 | }), [message]);
24 |
25 | return { outputP, view };
26 | }));
27 |
28 | export default defineTool({ name: 'not-found', makeProgram, collectReferences, run })
29 |
--------------------------------------------------------------------------------
/packages/original-tools/src/npm/index.tsx:
--------------------------------------------------------------------------------
1 | import { ControlledTextInput } from "@engraft/shared/lib/ControlledTextInput.js";
2 | import { CollectReferences, EngraftPromise, MakeProgram, ToolRun, ToolView, defineTool, hookMemo, hooks, memoizeProps, renderWithReact, up } from "@engraft/toolkit";
3 |
4 | type Program = {
5 | toolName: 'npm',
6 | packageName: string,
7 | }
8 |
9 | const makeProgram: MakeProgram = () => ({
10 | toolName: 'npm',
11 | packageName: '',
12 | });
13 |
14 | const collectReferences: CollectReferences = (_program) => [];
15 |
16 | const run: ToolRun = memoizeProps(hooks((props) => {
17 | const { program } = props;
18 |
19 | // TODO: debouncing?
20 | // all sorts of caching, error detection, etc.
21 | // package search
22 | // automatic .default?
23 |
24 | const outputP = hookMemo(() => EngraftPromise.try(() => {
25 | // TODO: The `import` below doesn't work in lib2 output... hmm...
26 | // const url = `https://cdn.skypack.dev/${name}`;
27 | const url = `https://esm.sh/${program.packageName}`;
28 | return import(/* @vite-ignore */ url).then((module) => ({value: module}));
29 | }), [program.packageName]);
30 |
31 | const view: ToolView = hookMemo(() => ({
32 | render: renderWithReact(({updateProgram}) =>
33 |
34 |
35 | name up(updateProgram).packageName.$set(ev.target.value)} />
36 |
37 |
38 | ),
39 | }), [program.packageName]);
40 |
41 | return { outputP, view };
42 | }));
43 |
44 | export default defineTool({ name: 'npm', makeProgram, collectReferences, run })
45 |
--------------------------------------------------------------------------------
/packages/original-tools/src/request/RowToCol.tsx:
--------------------------------------------------------------------------------
1 | import { HTMLProps, memo, useEffect } from "react"
2 | import { useSize } from "@engraft/shared/lib/useSize.js"
3 |
4 | export const RowToCol = memo(function RowToCol(props: HTMLProps & {
5 | minRowWidth: number,
6 | reportIsCol?: (isCol: boolean) => void,
7 | }) {
8 | const {children, className, minRowWidth, reportIsCol, ...restProps} = props
9 |
10 | const [sizeRef, size] = useSize();
11 |
12 | const isCol = size ? size.width < minRowWidth : false;
13 |
14 | useEffect(() => {
15 | if (reportIsCol) {
16 | reportIsCol(isCol);
17 | }
18 | }, [reportIsCol, size, isCol]);
19 |
20 | return (
21 |
22 | {children}
23 |
24 | );
25 | })
26 |
27 |
28 |
--------------------------------------------------------------------------------
/packages/original-tools/src/simulation/TODO.md:
--------------------------------------------------------------------------------
1 | [ ] init output doesn't reflect in on-tick input?
2 | [ ] debug perf
3 | [ ] "auto-play" tick
4 |
--------------------------------------------------------------------------------
/packages/original-tools/src/test-show-program/TODO.md:
--------------------------------------------------------------------------------
1 | [ ] drag causes vibration
2 |
--------------------------------------------------------------------------------
/packages/original-tools/src/test-show-program/index.tsx:
--------------------------------------------------------------------------------
1 | import { CollectReferences, defineTool, hookMemo, hookRunTool, hooks, MakeProgram, memoizeProps, renderWithReact, ShowView, ToolProgram, ToolRun, ToolView, up, ValueEditable } from "@engraft/toolkit";
2 |
3 | type Program = {
4 | toolName: 'test-show-program',
5 | subProgram: ToolProgram,
6 | }
7 |
8 | const makeProgram: MakeProgram = (context, defaultInputCode) => ({
9 | toolName: 'test-show-program',
10 | subProgram: context.makeSlotWithCode(defaultInputCode || ''),
11 | });
12 |
13 | const collectReferences: CollectReferences = (program) => program.subProgram;
14 |
15 | const run: ToolRun = memoizeProps(hooks((props) => {
16 | const { program, varBindings, context } = props;
17 |
18 | const subResult = hookRunTool({program: program.subProgram, varBindings, context})
19 |
20 | const outputP = subResult.outputP;
21 |
22 | const view: ToolView = hookMemo(() => ({
23 | render: renderWithReact(({updateProgram}) =>
24 |
25 |
26 |
27 |
28 | ),
29 | }), [program.subProgram, subResult.view]);
30 |
31 | return { outputP, view };
32 | }));
33 |
34 | export default defineTool({ name: 'test-show-program', makeProgram, collectReferences, run });
35 |
--------------------------------------------------------------------------------
/packages/original-tools/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/original-tools/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/pyodide/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/pyodide",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Pyodide integration for Engraft",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "pyodide": "0.23.2"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/pyodide/src/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'https://*';
--------------------------------------------------------------------------------
/packages/pyodide/src/index.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import type { PyodideInterface } from "pyodide/pyodide.js";
3 |
4 | let _pyodide: PyodideInterface | undefined = undefined;
5 |
6 | export async function getPyodide() : Promise {
7 | if (_pyodide === undefined) {
8 | const isNode = (globalThis as any)?.process?.release?.name === 'node';
9 | const pyodideModule = isNode
10 | ? await import("pyodide/pyodide.js")
11 | : await import("https://cdn.jsdelivr.net/pyodide/v0.23.1/full/pyodide.mjs");
12 | _pyodide = await pyodideModule.loadPyodide() as PyodideInterface;
13 | const originalConsoleLog = console.log;
14 | console.log = () => {};
15 | await _pyodide.loadPackage("numpy");
16 | console.log = originalConsoleLog;
17 | }
18 | return _pyodide;
19 | }
20 |
--------------------------------------------------------------------------------
/packages/pyodide/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/pyodide/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/react",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Basic utilities for working with Engraft in React",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/core": "^0.0.9",
21 | "@engraft/refunc": "^0.0.9"
22 | },
23 | "peerDependencies": {
24 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc",
25 | "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/react/src/EngraftPromise-react.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 | import { EngraftPromise, PromiseState } from "@engraft/core";
3 |
4 | export function usePromiseState(promise: EngraftPromise): PromiseState {
5 | const [state, setState] = useState>(() => EngraftPromise.state(promise));
6 |
7 | useEffect(() => {
8 | let upToDate = true;
9 | const update = () => {
10 | if (upToDate) {
11 | setState(EngraftPromise.state(promise));
12 | }
13 | };
14 | promise.then(update, update);
15 | update();
16 | return () => { upToDate = false; };
17 | }, [promise]);
18 |
19 | return state;
20 | };
21 |
22 | // no reason to `memo()` this; the `children` function will always be fresh anyway
23 | export function UsePromiseState(props: {
24 | promise: EngraftPromise,
25 | children: (state: PromiseState) => React.ReactElement | null,
26 | }) {
27 | const state = usePromiseState(props.promise);
28 | return props.children(state);
29 | }
30 |
--------------------------------------------------------------------------------
/packages/react/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/refunc-react/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/refunc-react",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "React features for Refunc",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/refunc": "^0.0.9"
21 | },
22 | "devDependencies": {
23 | "react-test-renderer": "18.2.0"
24 | },
25 | "peerDependencies": {
26 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/refunc-react/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "@engraft/refunc";
2 | export * from "./useRefunction.js";
3 |
--------------------------------------------------------------------------------
/packages/refunc-react/src/useRefunction.ts:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import { Refunction, RefuncMemory } from "@engraft/refunc";
3 |
4 | export function useRefunction(f: Refunction, ...args: Args) {
5 | const memoryRef = useRef(new RefuncMemory());
6 | return f(memoryRef.current, ...args);
7 | }
8 |
--------------------------------------------------------------------------------
/packages/refunc-react/test/useRefunction.test.tsx:
--------------------------------------------------------------------------------
1 | import { Refunction, memoize } from "@engraft/refunc";
2 | import { useRefunction } from "../lib/useRefunction.js";
3 | import React, { Fragment } from "react";
4 | import TestRenderer from "react-test-renderer";
5 | import { describe, expect, it } from "vitest";
6 |
7 | describe('useRefunction', () => {
8 | it('basically works', () => {
9 | let squareRuns = 0;
10 | const squareRefunc = memoize(Refunction.fromFunction((x: number) => {
11 | squareRuns++;
12 | return x * x;
13 | }));
14 |
15 | let output: number | null = null;
16 | const MyComponent = (props: {x: number}) => {
17 | output = useRefunction(squareRefunc, props.x);
18 | return null;
19 | };
20 |
21 | const testRenderer = TestRenderer.create();
22 |
23 | testRenderer.update();
24 | expect(output).toBe(4);
25 | expect(squareRuns).toBe(1);
26 |
27 | testRenderer.update();
28 | expect(output).toBe(9);
29 | expect(squareRuns).toBe(2);
30 |
31 | testRenderer.update();
32 | expect(output).toBe(9);
33 | expect(squareRuns).toBe(2);
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/packages/refunc-react/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/refunc-react/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/refunc/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/refunc",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Library for incremental computation",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/shared": "^0.0.9"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/packages/refunc/src/hooks-debug.ts:
--------------------------------------------------------------------------------
1 | import { compare } from "@engraft/shared/lib/compare.js";
2 | import { hookRef } from "./hooks.js";
3 |
4 | // Not especially pure; probably shouldn't be used outside of debug contexts?
5 | export function hookPrevious (value: T, init: () => T): T {
6 | const ref = hookRef(init);
7 | const current = ref.current;
8 | ref.current = value;
9 | return current;
10 | };
11 |
12 | // Utility for debugging what might be causing a memoized mento to re-run.
13 | // Just call:
14 | // hookLogChanges({someVar, someOtherVar, ...})
15 | export function hookLogChanges(values: any, label?: string) {
16 | const prevValues = hookPrevious(values, () => null);
17 | if (!prevValues) { return; }
18 | for (const key in values) {
19 | if (values[key] !== prevValues[key]) {
20 | console.groupCollapsed(`${label ? `(${label}) ` : ''}${key}: ${prevValues[key]} → ${values[key]}`);
21 | try {
22 | console.log(compare(prevValues[key], values[key]));
23 | } finally {
24 | console.groupEnd();
25 | }
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/refunc/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./refunc.js";
2 | export * from "./memoize.js";
3 | export * from "./hooks.js";
4 | export * from "./hookMemo.js";
5 | export * from "./hooks-debug.js";
6 |
--------------------------------------------------------------------------------
/packages/refunc/src/refunc.ts:
--------------------------------------------------------------------------------
1 | // Refunction: an unopinionated interface for a function that can remember its past executions
2 |
3 | export type Refunction = (memory: RefuncMemory, ...args: Args) => Return;
4 |
5 | export class RefuncMemory {
6 |
7 | }
8 |
9 | // For convenience...
10 |
11 | export type AnyFunction = (...args: any[]) => any;
12 |
13 | export type RefunctionLike any> = Refunction, ReturnType>;
14 |
15 | // eslint-disable-next-line @typescript-eslint/no-redeclare
16 | export const Refunction = {
17 | fromFunction(f: F): RefunctionLike {
18 | return (_memory: RefuncMemory, ...args: Parameters) => {
19 | return f(...args);
20 | };
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/packages/refunc/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/refunc/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/shared/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/shared",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Shared utilities",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "type": "module",
18 | "dependencies": {
19 | "deep-object-diff": "^1.1.9",
20 | "lodash": "^4.17.21",
21 | "lodash-es": "^4.17.21",
22 | "react": "^18.0.0",
23 | "react-contenteditable": "^3.3.6",
24 | "react-dom": "^18.0.0",
25 | "react-error-boundary": "^3.1.4"
26 | },
27 | "devDependencies": {
28 | "@types/lodash-es": "^4.17.7"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/shared/src/DOM.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | interface Props extends React.HTMLProps {
4 | element: HTMLElement | SVGSVGElement | undefined,
5 | }
6 |
7 | export function DOM({element, ...rest}: Props) {
8 | const [container, setContainer] = React.useState(null)
9 |
10 | React.useEffect(() => {
11 | if (container) {
12 | while (container.lastChild) {
13 | container.removeChild(container.lastChild);
14 | }
15 | if (element) {
16 | if (element.isConnected) {
17 | container.appendChild(element.cloneNode(true));
18 | } else {
19 | container.appendChild(element);
20 | }
21 | }
22 | }
23 | }, [container, element])
24 |
25 | return
26 | }
27 |
--------------------------------------------------------------------------------
/packages/shared/src/ErrorBoundary.tsx:
--------------------------------------------------------------------------------
1 | import { memo, ReactNode } from "react";
2 | import { ErrorBoundary as ErrorBoundaryFromLib, FallbackProps } from "react-error-boundary";
3 |
4 | interface ErrorBoundaryProps {
5 | children: ReactNode,
6 | }
7 |
8 | export const ErrorBoundary = memo(({children}: ErrorBoundaryProps) => {
9 | return
13 | {children}
14 |
15 | });
16 |
17 | const FallbackComponent = memo(({error}: FallbackProps) => {
18 | return
19 |
🚨 React error:
20 |
{error.message}
21 |
22 | });
23 |
--------------------------------------------------------------------------------
/packages/shared/src/OrError.ts:
--------------------------------------------------------------------------------
1 | import { hasProperty } from "./hasProperty.js";
2 |
3 | export type OrError = {value: T} | {error: unknown};
4 |
5 | // eslint-disable-next-line @typescript-eslint/no-redeclare
6 | export const OrError = {
7 | try(f: () => T): OrError {
8 | try {
9 | return {value: f()};
10 | } catch (e) {
11 | return {error: e};
12 | }
13 | },
14 | orThrow(f: OrError): T {
15 | if (hasProperty(f, 'error')) {
16 | throw f.error;
17 | } else {
18 | return f.value;
19 | }
20 | },
21 | };
22 |
--------------------------------------------------------------------------------
/packages/shared/src/ShadowDOM.tsx:
--------------------------------------------------------------------------------
1 | import { HTMLProps, memo, useCallback, useState } from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | interface Props extends HTMLProps {
5 | }
6 |
7 | export const ShadowDOM = memo(function ShadowDOM({children, ...rest}: Props) {
8 | const [shadowRoot, setShadowRoot] = useState(null);
9 |
10 | const onDiv = useCallback((div: HTMLDivElement | null) => {
11 | if (div) {
12 | setShadowRoot(div.shadowRoot || div.attachShadow({ mode: 'open' }));
13 |
14 | } else {
15 | setShadowRoot(null);
16 | }
17 | }, [])
18 |
19 | return <>
20 |
21 | {shadowRoot && ReactDOM.createPortal(children, shadowRoot as unknown as Element)}
22 | >;
23 | });
24 |
--------------------------------------------------------------------------------
/packages/shared/src/Updater.ts:
--------------------------------------------------------------------------------
1 | export type Updater = (f: (oldU: U) => T) => void;
2 | // (use U for when you know the updater is coming from an even narrower type than the output)
3 |
4 | export type Setter = (newT: T) => void;
5 |
--------------------------------------------------------------------------------
/packages/shared/src/Use.tsx:
--------------------------------------------------------------------------------
1 | import { ReactElement } from "react";
2 |
3 | export function Use(props: {hook: () => Return, children: (ret: Return) => ReactElement}): ReactElement;
4 | export function Use(props: {hook: (...args: Args) => Return, args: Args, children: (ret: Return) => ReactElement}): ReactElement;
5 | export function Use(props: {hook: (...args: Args) => Return, args?: Args, children: (ret: Return) => ReactElement}) {
6 | const {hook, args, children} = props;
7 | const ret = hook(...args || ([] as any as Args));
8 | return children(ret);
9 | }
10 |
11 | export function hookToComponent(hook: () => T) {
12 | return ({children}: {children: (t: T) => ReactElement}) =>
13 | ;
14 | }
15 |
--------------------------------------------------------------------------------
/packages/shared/src/assert.ts:
--------------------------------------------------------------------------------
1 | export function assert(condition: boolean, message?: string): asserts condition {
2 | if (!condition) {
3 | throw new Error(message || 'Assertion failed');
4 | }
5 | }
6 |
7 | export function assertNever(_never: never, message?: string): never {
8 | throw new Error(message || `Reached unreachable code: unexpected value ${_never}`);
9 | }
10 |
--------------------------------------------------------------------------------
/packages/shared/src/cache.ts:
--------------------------------------------------------------------------------
1 | import { OrError } from "./OrError.js";
2 |
3 | // TODO: this will just fill up... someday we should be Principled
4 | export function cache(f: (arg: string) => Return): (arg: string) => Return {
5 | const _cache: {[arg: string]: OrError} = {};
6 | return (arg: string) => {
7 | let cached = _cache[arg];
8 | if (!cached) {
9 | cached = _cache[arg] = OrError.try(() => f(arg));
10 | }
11 | return OrError.orThrow(cached);
12 | }
13 | }
14 |
15 | export function weakMapCache(f: (arg: Arg) => Return): (arg: Arg) => Return {
16 | const _cache = new WeakMap>();
17 | return (arg: Arg) => {
18 | let cached = _cache.get(arg);
19 | if (!cached) {
20 | cached = OrError.try(() => f(arg));
21 | _cache.set(arg, cached);
22 | }
23 | return OrError.orThrow(cached);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/shared/src/compare.ts:
--------------------------------------------------------------------------------
1 | import * as IsEqual from "lodash-es/isEqual.js";
2 | import { diff } from "deep-object-diff";
3 |
4 | // TODO: what hath ESM wrought?
5 | const isEqual = IsEqual.default as unknown as typeof import("lodash-es/isEqual.js").default;
6 |
7 | export function compare(a: any, b: any): string {
8 | if (!isEqual(a, b)) {
9 | return "[not deep-equal]: " + JSON.stringify(diff(a, b), null, 2);
10 | } else {
11 | return "[deep-equal]: " + JSON.stringify(compareDeepEqualObjectsForReferenceEquality(a, b), null, 2);
12 | }
13 | }
14 |
15 | // this crude-but-helpful li'l guy shows you how deep you need to go to get reference equality.
16 | function compareDeepEqualObjectsForReferenceEquality(a: any, b: any): any {
17 | // we know they're deep-equal...
18 | // are they ref-equal?
19 |
20 | if (a === b) { return '[ref-equal]'; }
21 |
22 | // ok so they're deep-equal but not ref-equal
23 | // they must either be both arrays or both objects
24 |
25 | if (Array.isArray(a)) {
26 | return a.map((a, i) => compareDeepEqualObjectsForReferenceEquality(a, b[i]));
27 | } else {
28 | const result: any = {};
29 | for (const key in a) {
30 | result[key] = compareDeepEqualObjectsForReferenceEquality(a[key], b[key]);
31 | }
32 | return result;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/shared/src/compile.ts:
--------------------------------------------------------------------------------
1 | import { cache } from "./cache.js";
2 |
3 | export function compileExpression(exprCode: string): (context: object) => unknown {
4 | // eslint-disable-next-line no-new-func
5 | return new Function('__context__', `with (__context__) { return (${exprCode}); }`) as any
6 | }
7 |
8 | export const compileExpressionCached = cache(compileExpression);
9 |
10 | export function compileBody(body: string): (context: object) => unknown {
11 | // eslint-disable-next-line no-new-func
12 | return new Function('__context__', `with (__context__) { ${body} }`) as any
13 | }
14 |
15 | export const compileBodyCached = cache(compileBody);
16 |
--------------------------------------------------------------------------------
/packages/shared/src/count.ts:
--------------------------------------------------------------------------------
1 | export function count(num: number, singular: string, plural: string) {
2 | return num + ' ' + (num === 1 ? singular : plural);
3 | }
--------------------------------------------------------------------------------
/packages/shared/src/hasProperty.ts:
--------------------------------------------------------------------------------
1 | import { isObject } from "./isObject.js";
2 |
3 | export function hasProperty(obj: unknown, key: K): obj is Record {
4 | return isObject(obj) && key in obj;
5 | }
6 |
--------------------------------------------------------------------------------
/packages/shared/src/isArray.ts:
--------------------------------------------------------------------------------
1 | // The built-in types for Array.isArray says it returns any[], which is not great.
2 |
3 | export function isArray(x: unknown): x is unknown[] {
4 | return Array.isArray(x);
5 | }
6 |
--------------------------------------------------------------------------------
/packages/shared/src/isObject.ts:
--------------------------------------------------------------------------------
1 | export function isObject(x: unknown): x is object {
2 | return typeof x === 'object' && x !== null;
3 | }
4 |
--------------------------------------------------------------------------------
/packages/shared/src/isoformat.ts:
--------------------------------------------------------------------------------
1 | // adapted from https://github.com/mbostock/isoformat/
2 |
3 | export function isoformat(date: Date | number): string | null {
4 | if (!(date instanceof Date)) date = new Date(+date);
5 | if (isNaN(+date)) return null;
6 | const hours = date.getUTCHours();
7 | const minutes = date.getUTCMinutes();
8 | const seconds = date.getUTCSeconds();
9 | const milliseconds = date.getUTCMilliseconds();
10 | return `${formatYear(date.getUTCFullYear())}-${pad(date.getUTCMonth() + 1, 2)}-${pad(date.getUTCDate(), 2)}${
11 | hours || minutes || seconds || milliseconds ? `T${pad(hours, 2)}:${pad(minutes, 2)}${
12 | seconds || milliseconds ? `:${pad(seconds, 2)}${
13 | milliseconds ? `.${pad(milliseconds, 3)}` : ``
14 | }` : ``
15 | }Z` : ``
16 | }`;
17 | }
18 |
19 | function formatYear(year: number) {
20 | return year < 0 ? `-${pad(-year, 6)}`
21 | : year > 9999 ? `+${pad(year, 6)}`
22 | : pad(year, 4);
23 | }
24 |
25 | function pad(value: number, width: number) {
26 | return `${value}`.padStart(width, "0");
27 | }
28 |
--------------------------------------------------------------------------------
/packages/shared/src/noOp.ts:
--------------------------------------------------------------------------------
1 | export function noOp () {
2 | return;
3 | }
4 |
5 | export function identity(x: T): T {
6 | return x;
7 | }
8 |
9 | export const empty = Object.freeze({});
10 |
--------------------------------------------------------------------------------
/packages/shared/src/normalizeIndent.ts:
--------------------------------------------------------------------------------
1 | import { assert } from "./assert.js";
2 |
3 | function noop(strings: TemplateStringsArray, ...expressions: unknown[]): string {
4 | let result = strings[0];
5 |
6 | for (let i = 1, l = strings.length; i < l; i++) {
7 | result += expressions[i - 1];
8 | result += strings[i];
9 | }
10 |
11 | return result;
12 | };
13 |
14 | export function normalizeIndent(strings: TemplateStringsArray, ...expressions: unknown[]) {
15 | const text = noop(strings, ...expressions);
16 | let textLines = text.split('\n');
17 | assert(textLines[0] === '', 'newline right after opening backtick please');
18 | textLines = textLines.slice(1);
19 | assert(textLines[textLines.length - 1].match(/^ *$/) !== null, 'no non-whitespace on closing backtick line please');
20 | textLines = textLines.slice(0, textLines.length - 1);
21 | const leftPadding = Math.min(...textLines.map(line => {
22 | const m = line.match(/^( *)[^ ]/); // leading spaces and then a non-space
23 | if (m === null) { return Infinity; } // lines without non-spaces don't constrain
24 | return m[1].length;
25 | }));
26 | return textLines.map(line => line.slice(leftPadding)).join('\n') + '\n';
27 | }
28 |
--------------------------------------------------------------------------------
/packages/shared/src/runtimeObjectId.ts:
--------------------------------------------------------------------------------
1 | export function runtimeObjectId(obj: any): number {
2 | const fromMap = runtimeObjectIdMap.get(obj);
3 | if (fromMap !== undefined) {
4 | return fromMap;
5 | } else {
6 | const objId = nextId;
7 | nextId++;
8 | runtimeObjectIdMap.set(obj, objId);
9 | return objId;
10 | }
11 | }
12 |
13 | let nextId = 0;
14 | const runtimeObjectIdMap = new WeakMap();
15 |
--------------------------------------------------------------------------------
/packages/shared/src/saveFile.ts:
--------------------------------------------------------------------------------
1 | export function saveFile (contents: Blob, fileName: string) {
2 | let dummyLink = document.createElement("a")
3 | dummyLink.href = URL.createObjectURL(contents)
4 | dummyLink.download = fileName
5 | dummyLink.click()
6 | URL.revokeObjectURL(dummyLink.href);
7 | }
8 |
9 | // can make Blob from contents with
10 | // new Blob([contents], {type})
11 | // type is something funky like "application/json;charset=utf-8"
12 |
--------------------------------------------------------------------------------
/packages/shared/src/sets.ts:
--------------------------------------------------------------------------------
1 | // adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
2 |
3 | export function union(...as: Iterable[]): Set {
4 | const _union = new Set();
5 | for (const a of as) {
6 | for (const elem of a) {
7 | _union.add(elem);
8 | }
9 | }
10 | return _union;
11 | }
12 |
13 | export function intersection(a: Iterable, b: Iterable): Set {
14 | const _intersection = new Set();
15 | const aSet = a instanceof Set ? a as Set : new Set(a);
16 | for (const elem of b) {
17 | if (aSet.has(elem)) {
18 | _intersection.add(elem);
19 | }
20 | }
21 | return _intersection;
22 | }
23 |
24 | export function symmetricDifference(a: Iterable, b: Iterable): Set {
25 | const _difference = new Set(a);
26 | for (const elem of b) {
27 | if (_difference.has(elem)) {
28 | _difference.delete(elem);
29 | } else {
30 | _difference.add(elem);
31 | }
32 | }
33 | return _difference;
34 | }
35 |
36 | export function difference(a: Iterable, b: Iterable): Set {
37 | const _difference = new Set(a);
38 | for (const elem of b) {
39 | _difference.delete(elem);
40 | }
41 | return _difference;
42 | }
43 |
--------------------------------------------------------------------------------
/packages/shared/src/unusedLabel.ts:
--------------------------------------------------------------------------------
1 | import _ from "lodash";
2 |
3 | const letters = _.range(65, 91).map((n) => String.fromCharCode(n));
4 | export const alphaLabels = [
5 | ...letters,
6 | ...letters.flatMap((a) => letters.map((b) => a + b)),
7 | ];
8 |
9 | export function unusedLabel(labels: string[], usedLabels: string[]): string | undefined;
10 | export function unusedLabel(labels: string[], usedLabels: { [key: string]: unknown }): string | undefined;
11 | export function unusedLabel(labels: string[], usedLabels: string[] | { [key: string]: unknown }) {
12 | if (Array.isArray(usedLabels)) {
13 | return unusedLabel(labels, Object.fromEntries(usedLabels.map((label) => [label, true])));
14 | } else {
15 | return labels.find((label) => !(label in usedLabels));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/packages/shared/src/useDedupe.tsx:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import { Eq } from "./eq.js";
3 |
4 | export function useDedupe(t: T, eq: Eq): T {
5 | const lastT = useRef();
6 |
7 | if (!lastT.current || (t !== lastT.current && !eq(t, lastT.current))) {
8 | lastT.current = t;
9 | }
10 |
11 | return lastT.current;
12 | }
13 |
--------------------------------------------------------------------------------
/packages/shared/src/useEventListener.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 |
3 | export function useWindowEventListener(type: K, listener: (ev: WindowEventMap[K]) => void, win: Window = window) {
4 | useEffect(() => {
5 | win.addEventListener(type, listener);
6 | return () => {
7 | win.removeEventListener(type, listener);
8 | };
9 | }, [listener, type, win]);
10 | }
11 |
12 | export function useElementEventListener(element: HTMLElement | null, type: K, listener: (ev: HTMLElementEventMap[K]) => void) {
13 | useEffect(() => {
14 | if (element) {
15 | element.addEventListener(type, listener);
16 | return () => {
17 | element.removeEventListener(type, listener);
18 | };
19 | }
20 | }, [element, listener, type]);
21 | }
22 |
--------------------------------------------------------------------------------
/packages/shared/src/useHover.tsx:
--------------------------------------------------------------------------------
1 | import { useCallback, useLayoutEffect, useState } from "react";
2 | import { useElementEventListener } from "./useEventListener.js";
3 |
4 | let EVENT_LISTENER_CREATED = false;
5 | let mouseClientX: number;
6 | let mouseClientY: number;
7 |
8 | export function useHover(): [(elem: HTMLElement | null) => void, boolean, HTMLElement | null] {
9 | if (!EVENT_LISTENER_CREATED) {
10 | EVENT_LISTENER_CREATED = true;
11 | document.addEventListener('mousemove', (e) => {
12 | mouseClientX = e.clientX;
13 | mouseClientY = e.clientY;
14 | })
15 | }
16 |
17 | const [isHovered, setIsHovered] = useState(false);
18 | const [elem, setElem] = useState(null);
19 |
20 | useLayoutEffect(() => {
21 | if (elem) {
22 | let box = elem.getBoundingClientRect();
23 | setIsHovered(
24 | box.left <= mouseClientX && mouseClientX <= box.right &&
25 | box.top <= mouseClientY && mouseClientY <= box.bottom
26 | )
27 | }
28 | }, [elem])
29 |
30 | useElementEventListener(elem, 'mouseenter', useCallback(() => setIsHovered(true), []));
31 | useElementEventListener(elem, 'mouseleave', useCallback(() => setIsHovered(false), []));
32 |
33 | return [setElem, isHovered, elem];
34 | }
35 |
--------------------------------------------------------------------------------
/packages/shared/src/useKeyHeld.tsx:
--------------------------------------------------------------------------------
1 | import { useCallback, useState } from "react";
2 | import { useWindowEventListener } from "./useEventListener.js";
3 |
4 | // Known limitation: When you leave a window and come back later, we don't know
5 | // which keys are held so we assume none are.
6 |
7 | // Known limitation: Cmd-tab doesn't trigger window blur, but it does capture
8 | // the key-up for cmd if you come back to the original tab.
9 |
10 | export function useKeyHeld(targetKey: string) {
11 | const [keyHeld, setKeyHeld] = useState(false);
12 |
13 | useWindowEventListener('keydown',
14 | useCallback((ev) => {
15 | if (ev.key === targetKey) {
16 | setKeyHeld(true);
17 | }
18 | }, [targetKey])
19 | );
20 |
21 | useWindowEventListener('keyup',
22 | useCallback((ev) => {
23 | if (ev.key === targetKey) {
24 | setKeyHeld(false);
25 | }
26 | }, [targetKey])
27 | );
28 |
29 | useWindowEventListener('blur',
30 | useCallback(() => {
31 | setKeyHeld(false);
32 | }, [])
33 | );
34 |
35 | return keyHeld;
36 | }
37 |
--------------------------------------------------------------------------------
/packages/shared/src/useLocalStorage.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react";
2 |
3 | // note: this only checks local storage at initialization, not continuously.
4 | // (that would be cool tho; see "StorageItem" in a different project.)
5 | // it's also inefficient.
6 | // hope u like it tho.
7 |
8 | export function useLocalStorage(
9 | key: string,
10 | init: () => T,
11 | parse: (s: string) => T = JSON.parse,
12 | stringify: (t: T) => string = JSON.stringify
13 | ) {
14 | const [t, setT] = useState(() => {
15 | try {
16 | const str = window.localStorage.getItem(key)
17 | if (str) {
18 | return parse(str);
19 | }
20 | } catch {}
21 | return init();
22 | });
23 |
24 | useEffect(() => {
25 | try {
26 | window.localStorage.setItem(key, stringify(t));
27 | } catch (e) {
28 | console.warn("error saving to local storage", e);
29 | }
30 | }, [key, stringify, t]);
31 |
32 | return [t, setT] as const;
33 | }
34 |
--------------------------------------------------------------------------------
/packages/shared/src/useRefForCallback.tsx:
--------------------------------------------------------------------------------
1 | import { MutableRefObject, useRef } from "react";
2 |
3 | // So you want to define a ref that's always kept in sync with a certain value.
4 | // You wanna do this so that a callback can use the up-to-date value, without
5 | // having to be redefined when the value changes.
6 |
7 | // Here's useRefForCallback!
8 |
9 | export function useRefForCallback(value: T): MutableRefObject {
10 | const ref = useRef(value);
11 | ref.current = value;
12 | return ref;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/shared/src/useSize.tsx:
--------------------------------------------------------------------------------
1 | import { useLayoutEffect, useState } from "react";
2 |
3 | export function useSize(): [(elem: HTMLElement | null) => void, DOMRectReadOnly | undefined] {
4 | const [domRect, setDomRect] = useState(undefined);
5 | const [elem, setElem] = useState(null);
6 |
7 | useLayoutEffect(() => {
8 | if (elem) {
9 | const observer = new ResizeObserver((entries) => setDomRect(entries[0].contentRect));
10 | observer.observe(elem);
11 | return () => {
12 | observer.disconnect();
13 | setDomRect(undefined);
14 | }
15 | }
16 | }, [elem])
17 |
18 | return [setElem, domRect];
19 | }
20 |
--------------------------------------------------------------------------------
/packages/shared/test/eq.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "vitest";
2 | import { objEqWithRefEq } from "../lib/eq.js";
3 |
4 | describe('objEqWithRefEq', () => {
5 | it('is not dumb about undefined', () => {
6 | // embarrassed to report this is here for a good reason
7 | expect(objEqWithRefEq({a: undefined}, {b: undefined})).toBe(false);
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/packages/shared/test/isObject.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "vitest";
2 | import { isObject } from "../lib/isObject.js";
3 |
4 | describe('isObject', () => {
5 | it('basically works', () => {
6 | expect(isObject({})).toBe(true);
7 | expect(isObject([])).toBe(true);
8 | expect(isObject(() => {})).toBe(false);
9 | expect(isObject(null)).toBe(false);
10 | expect(isObject(undefined)).toBe(false);
11 | expect(isObject(123)).toBe(false);
12 | expect(isObject('abc')).toBe(false);
13 | expect(isObject(true)).toBe(false);
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/packages/shared/test/normalizeIndent.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "vitest";
2 | import { normalizeIndent } from "../lib/normalizeIndent.js";
3 |
4 |
5 | describe('normalizeIndent', () => {
6 | it('basically works', () => {
7 | expect(normalizeIndent`
8 | a
9 | b
10 | `).toBe('a\nb\n');
11 |
12 | expect(normalizeIndent`
13 | a
14 | b
15 | `).toBe('a\n b\n');
16 |
17 | expect(normalizeIndent`
18 | a
19 | b
20 | `).toBe(' a\nb\n');
21 | });
22 |
23 | it('is strict about first and last lines', () => {
24 | expect(() => {
25 | normalizeIndent`extra
26 | a
27 | `;
28 | }).toThrow();
29 |
30 | expect(() => {
31 | normalizeIndent`
32 | a
33 | extra`;
34 | }).toThrow();
35 | });
36 |
37 | it('is ok with blank lines', () => {
38 | expect(normalizeIndent`
39 | a
40 |
41 | b
42 | `).toBe('a\n\nb\n');
43 | })
44 | });
45 |
--------------------------------------------------------------------------------
/packages/shared/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/shared/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/testbed/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Engraft testbed
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/packages/testbed/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "name": "@engraft/testbed",
4 | "version": "0.0.9",
5 | "license": "MIT",
6 | "description": "Engraft testbed web application.",
7 | "scripts": {
8 | "depcheck": "node ../../scripts/our-depcheck.cjs",
9 | "lint": "eslint --max-warnings=0 .",
10 | "tsc": "tsc",
11 | "build-app": "vite build --config ../../vite.config.ts",
12 | "preview": "vite preview --config ../../vite.config.ts",
13 | "dev": "vite --config ../../vite.config.ts"
14 | },
15 | "type": "module",
16 | "dependencies": {
17 | "@engraft/fancy-setup": "^0.0.9",
18 | "@engraft/hostkit": "^0.0.9",
19 | "@engraft/shared": "^0.0.9",
20 | "react": "^18.0.0",
21 | "react-dom": "^18.0.0",
22 | "react-error-boundary": "^3.1.4",
23 | "react-router-dom": "^6.3.0"
24 | },
25 | "devDependencies": {
26 | "@types/react-dom": "^18.0.0",
27 | "vite": "^4.3.2"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/testbed/src/App.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 100px;
3 | }
4 |
5 | html.darkMode {
6 | background-color: white;
7 | filter: invert() contrast(0.7);
8 | }
9 |
--------------------------------------------------------------------------------
/packages/testbed/src/examples/2023-04-26-python.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolName": "slot",
3 | "modeName": "tool",
4 | "subProgram": {
5 | "toolName": "notebook",
6 | "cells": [
7 | {
8 | "var_": {
9 | "id": "IDlettuce129029",
10 | "label": "A"
11 | },
12 | "program": {
13 | "toolName": "slot",
14 | "modeName": "tool",
15 | "subProgram": {
16 | "toolName": "python",
17 | "code": "import numpy as np\nnp.arange(12).reshape(3,4)",
18 | "subPrograms": {}
19 | },
20 | "defaultCode": ""
21 | }
22 | },
23 | {
24 | "var_": {
25 | "id": "IDfirefly964345",
26 | "label": "B"
27 | },
28 | "program": {
29 | "toolName": "slot",
30 | "modeName": "tool",
31 | "subProgram": {
32 | "toolName": "python",
33 | "code": "IDavocado332558 + IDpharaoh168742",
34 | "subPrograms": {
35 | "IDpharaoh168742": {
36 | "toolName": "slot",
37 | "modeName": "tool",
38 | "subProgram": {
39 | "toolName": "slider",
40 | "value": 2,
41 | "min": -10,
42 | "max": 10,
43 | "step": 1
44 | }
45 | }
46 | }
47 | },
48 | "defaultCode": "IDavocado332558"
49 | }
50 | }
51 | ],
52 | "prevVarId": "IDavocado332558"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/testbed/src/examples/index.ts:
--------------------------------------------------------------------------------
1 | import { ToolProgram } from "@engraft/hostkit";
2 |
3 | const modules = import.meta.glob('./*.json', { eager: true });
4 | export const examples =
5 | Object.entries(modules).map(([filename, program]) => ({
6 | name: filename.match(/\.\/(.*)\.json$/)![1],
7 | // vite seems to import json both directly and with `default`; let's just take default
8 | program: (program as {default: ToolProgram}).default,
9 | }));
10 |
--------------------------------------------------------------------------------
/packages/testbed/src/examples/menagerie-checkbox.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolName": "slot",
3 | "modeName": "tool",
4 | "subProgram": {
5 | "toolName": "notebook",
6 | "cells": [
7 | {
8 | "var_": {
9 | "id": "IDeyeliner690347",
10 | "label": "A"
11 | },
12 | "program": {
13 | "toolName": "slot",
14 | "modeName": "code",
15 | "code": "if (IDadvantage858860) {\n return \"yes please\"\n} else {\n return \"no thank you\"\n}",
16 | "defaultCode": "",
17 | "subPrograms": {
18 | "IDadvantage858860": {
19 | "toolName": "slot",
20 | "modeName": "tool",
21 | "subProgram": {
22 | "toolName": "checkbox",
23 | "checked": true
24 | }
25 | }
26 | }
27 | }
28 | }
29 | ],
30 | "prevVarId": "IDliver637520"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/packages/testbed/src/examples/menagerie-color.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolName": "slot",
3 | "modeName": "tool",
4 | "subProgram": {
5 | "toolName": "notebook",
6 | "cells": [
7 | {
8 | "var_": {
9 | "id": "IDsole978612",
10 | "label": "star"
11 | },
12 | "program": {
13 | "toolName": "slot",
14 | "modeName": "code",
15 | "code": "\n ★★★\n
",
16 | "defaultCode": "IDclover116038",
17 | "subPrograms": {
18 | "IDatom831336": {
19 | "toolName": "slot",
20 | "modeName": "tool",
21 | "subProgram": {
22 | "toolName": "color",
23 | "fields": {
24 | "r": 230,
25 | "g": 191,
26 | "b": 64
27 | },
28 | "subTools": {}
29 | }
30 | }
31 | }
32 | },
33 | "outputManualHeight": "infinity"
34 | }
35 | ],
36 | "prevVarId": "IDclover116038"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/testbed/src/examples/menagerie-example-datasets.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolName": "slot",
3 | "modeName": "tool",
4 | "subProgram": {
5 | "toolName": "notebook",
6 | "cells": [
7 | {
8 | "var_": {
9 | "id": "IDrabbit188545",
10 | "label": "A"
11 | },
12 | "program": {
13 | "toolName": "slot",
14 | "modeName": "tool",
15 | "subProgram": {
16 | "toolName": "example-datasets",
17 | "fields": {
18 | "datasetName": "olympians"
19 | },
20 | "subTools": {}
21 | },
22 | "defaultCode": ""
23 | },
24 | "outputManualHeight": 77
25 | },
26 | {
27 | "var_": {
28 | "id": "IDwaitress989419",
29 | "label": "B"
30 | },
31 | "program": {
32 | "toolName": "slot",
33 | "modeName": "tool",
34 | "subProgram": {
35 | "toolName": "data-table",
36 | "inputProgram": {
37 | "toolName": "slot",
38 | "modeName": "code",
39 | "code": "IDdolphin292158",
40 | "defaultCode": "IDdolphin292158",
41 | "subPrograms": {}
42 | },
43 | "transforms": {
44 | "sort": [],
45 | "filter": [],
46 | "names": []
47 | },
48 | "cellWidths": {}
49 | },
50 | "defaultCode": "IDdolphin292158"
51 | }
52 | }
53 | ],
54 | "prevVarId": "IDdolphin292158"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/packages/testbed/src/examples/menagerie-npm.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolName": "slot",
3 | "modeName": "tool",
4 | "subProgram": {
5 | "toolName": "notebook",
6 | "cells": [
7 | {
8 | "var_": {
9 | "id": "IDsailor264826",
10 | "label": "confetti"
11 | },
12 | "program": {
13 | "toolName": "slot",
14 | "modeName": "tool",
15 | "subProgram": {
16 | "toolName": "npm",
17 | "packageName": "canvas-confetti"
18 | },
19 | "defaultCode": ""
20 | }
21 | },
22 | {
23 | "var_": {
24 | "id": "IDgeese353480",
25 | "label": "button"
26 | },
27 | "program": {
28 | "toolName": "slot",
29 | "modeName": "code",
30 | "code": "",
31 | "defaultCode": "IDmagnesium183044",
32 | "subPrograms": {}
33 | }
34 | }
35 | ],
36 | "prevVarId": "IDmagnesium183044"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/packages/testbed/src/examples/menagerie-simple-chart.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolName": "slot",
3 | "modeName": "tool",
4 | "subProgram": {
5 | "toolName": "notebook",
6 | "cells": [
7 | {
8 | "var_": {
9 | "id": "IDamethyst894619",
10 | "label": "A"
11 | },
12 | "program": {
13 | "toolName": "slot",
14 | "modeName": "tool",
15 | "subProgram": {
16 | "toolName": "simple-chart",
17 | "dataProgram": {
18 | "toolName": "slot",
19 | "modeName": "tool",
20 | "subProgram": {
21 | "toolName": "example-datasets",
22 | "fields": {
23 | "datasetName": "cars"
24 | },
25 | "subTools": {}
26 | },
27 | "defaultCode": ""
28 | },
29 | "mark": "point",
30 | "xChannel": {
31 | "field": "economy (mpg)",
32 | "type": "quantitative"
33 | },
34 | "yChannel": {
35 | "field": "displacement (cc)",
36 | "type": "quantitative"
37 | }
38 | }
39 | },
40 | "outputManualHeight": "infinity"
41 | }
42 | ],
43 | "prevVarId": "IDface414118"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/testbed/src/examples/menagerie-slider.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolName": "slot",
3 | "modeName": "tool",
4 | "subProgram": {
5 | "toolName": "notebook",
6 | "cells": [
7 | {
8 | "var_": {
9 | "id": "IDamethyst894619",
10 | "label": "A"
11 | },
12 | "program": {
13 | "toolName": "slot",
14 | "modeName": "code",
15 | "code": "",
16 | "subPrograms": {
17 | "IDstetson765266": {
18 | "toolName": "slot",
19 | "modeName": "tool",
20 | "subProgram": {
21 | "toolName": "slider",
22 | "value": 40,
23 | "min": 0,
24 | "max": 50,
25 | "step": 1
26 | }
27 | }
28 | }
29 | },
30 | "outputManualHeight": "infinity"
31 | }
32 | ],
33 | "prevVarId": "IDface414118"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/testbed/src/examples/menagerie-synthesizer.json:
--------------------------------------------------------------------------------
1 | {
2 | "toolName": "slot",
3 | "modeName": "tool",
4 | "subProgram": {
5 | "toolName": "synthesizer",
6 | "inputProgram": {
7 | "toolName": "slot",
8 | "modeName": "code",
9 | "code": "\"Input Without Provided Output\"",
10 | "defaultCode": "",
11 | "subPrograms": {}
12 | },
13 | "code": "input.split(' ').map((x) => x[0]).join('')",
14 | "inOutPairs": [
15 | {
16 | "id": "IDtungsten196389",
17 | "inCode": "\"George Washington Carver\"",
18 | "outCode": "\"GWC\""
19 | },
20 | {
21 | "id": "IDarrow061111",
22 | "inCode": "\"David Attenborough\"",
23 | "outCode": "\"DA\""
24 | }
25 | ]
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/testbed/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createRoot } from "react-dom/client";
3 | import { HashRouter, Route, Routes } from "react-router-dom";
4 | import App from "./App.js";
5 |
6 | const root = createRoot(document.getElementById('root')!);
7 |
8 | root.render(
9 |
10 |
11 |
12 | }/>
13 | }/>
14 |
15 |
16 | ,
17 | );
18 |
--------------------------------------------------------------------------------
/packages/testbed/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/testbed/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "types": [
6 | "vite/client"
7 | ]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/packages/testing-setup/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/testing-setup",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Engraft components for testing purposes",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/toolkit": "^0.0.9"
21 | },
22 | "peerDependencies": {
23 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/testing-setup/src/index.ts:
--------------------------------------------------------------------------------
1 | import { Dispatcher, EngraftContext, Tool, forgetP } from "@engraft/toolkit";
2 | import * as TestingKnownOutput from "./testing-known-output.js";
3 | import * as TestingRefsFunc from "./testing-refs-func.js";
4 |
5 | export * as TestingKnownOutput from "./testing-known-output.js";
6 | export * as TestingRefsFunc from "./testing-refs-func.js";
7 |
8 | const tools: Tool[] = [
9 | forgetP(TestingKnownOutput.tool),
10 | forgetP(TestingRefsFunc.tool),
11 | ]
12 |
13 | export function makeTestingContext() {
14 | const context: EngraftContext = {
15 | dispatcher: new Dispatcher(),
16 | makeSlotWithCode: () => {
17 | throw new Error("Testing context does not support makeSlotWithCode");
18 | },
19 | makeSlotWithProgram: () => {
20 | throw new Error("Testing context does not support makeSlotWithProgram");
21 | },
22 | debugMode: false,
23 | };
24 |
25 | for (const tool of tools) {
26 | context.dispatcher.registerTool(tool);
27 | }
28 |
29 | return context
30 | }
31 |
--------------------------------------------------------------------------------
/packages/testing-setup/src/testing-known-output.tsx:
--------------------------------------------------------------------------------
1 | import { EngraftPromise, Tool, ToolOutput, ToolOutputView, ToolView, hookMemo, hooks, memoizeProps, renderWithReact } from "@engraft/toolkit";
2 |
3 | export type Program = {
4 | toolName: 'testing-known-output',
5 | outputP: EngraftPromise, // TODO: for testing, breaks serialization
6 | onRun?: () => void, // TODO: for testing, breaks serialization
7 | onViewRender?: () => void, // TODO: for testing, breaks serialization
8 | }
9 |
10 | export const tool: Tool = {
11 | name: 'testing-known-output',
12 |
13 | makeProgram: () => ({
14 | toolName: 'testing-known-output',
15 | outputP: EngraftPromise.unresolved(),
16 | }),
17 |
18 | collectReferences: () => [],
19 |
20 | run: memoizeProps(hooks((props) => {
21 | const { program } = props;
22 | const { outputP, onRun, onViewRender } = program;
23 |
24 | if (onRun) { onRun(); }
25 |
26 | const view: ToolView = hookMemo(() => ({
27 | render: renderWithReact(() => {
28 | if (onViewRender) { onViewRender(); }
29 | return
30 |
31 |
;
32 | }),
33 | }), [onViewRender, outputP]);
34 |
35 | return { outputP, view };
36 | })),
37 | };
38 |
--------------------------------------------------------------------------------
/packages/testing-setup/src/testing-refs-func.tsx:
--------------------------------------------------------------------------------
1 | import { EngraftPromise, Tool, ToolOutput, ToolProps, ToolView, hookMemo, hooks, memoizeProps, renderWithReact } from "@engraft/toolkit";
2 | import { Fragment } from "react";
3 |
4 | // NOTE: program is not serializable
5 | export type Program = {
6 | toolName: 'testing-refs-func',
7 | refs: string[],
8 | func?: (refOutputs: ToolOutput[]) => ToolOutput | EngraftPromise,
9 | onRun?: (props: ToolProps) => void,
10 | onViewRender?: () => void,
11 | }
12 |
13 | export const tool: Tool = {
14 | name: 'testing-refs-func',
15 |
16 | makeProgram: () => ({
17 | toolName: 'testing-refs-func',
18 | refs: [],
19 | func: () => EngraftPromise.unresolved(),
20 | }),
21 |
22 | collectReferences: (program) => program.refs.map(id => ({ id })),
23 |
24 | run: memoizeProps(hooks((props) => {
25 | const { program, varBindings } = props;
26 | const { refs, func, onRun, onViewRender } = program;
27 |
28 | if (onRun) { onRun(props); }
29 |
30 | const outputP =
31 | func
32 | ? EngraftPromise.all(refs.map(ref => varBindings[ref].outputP)).then(func)
33 | : EngraftPromise.unresolved();
34 |
35 | const view: ToolView = hookMemo(() => ({
36 | render: renderWithReact(() => {
37 | if (onViewRender) { onViewRender(); }
38 | return ;
39 | }),
40 | }), [onViewRender]);
41 |
42 | return { outputP, view };
43 | })),
44 | };
45 |
46 | export default tool;
47 |
--------------------------------------------------------------------------------
/packages/testing-setup/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/testing-setup/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-checkbox/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-checkbox",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Engraft checkbox tool",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/toolkit": "^0.0.9"
21 | },
22 | "peerDependencies": {
23 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/tool-checkbox/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { defineTool, EngraftPromise, hookMemo, hooks, memoizeProps, renderWithReact, ToolView } from "@engraft/toolkit";
2 |
3 | export type Program = {
4 | toolName: 'checkbox',
5 | checked: boolean,
6 | }
7 |
8 | export default defineTool({
9 | name: 'checkbox',
10 |
11 | makeProgram: () => ({
12 | toolName: 'checkbox',
13 | checked: false,
14 | }),
15 |
16 | collectReferences: () => [],
17 |
18 | run: memoizeProps(hooks((props) => {
19 | const { program } = props;
20 |
21 | const outputP = hookMemo(() => EngraftPromise.resolve({
22 | value: program.checked
23 | }), [program.checked]);
24 |
25 | const view: ToolView = hookMemo(() => ({
26 | render: renderWithReact(({updateProgram}) =>
27 | {
31 | updateProgram((old) => ({...old, checked: e.target.checked}))
32 | }}
33 | />
34 | ),
35 | }), [program.checked]);
36 |
37 | return { outputP, view };
38 | })),
39 | });
40 |
--------------------------------------------------------------------------------
/packages/tool-checkbox/test/index.test.ts:
--------------------------------------------------------------------------------
1 | import { EngraftPromise, toolFromModule } from "@engraft/core";
2 | import { RefuncMemory } from "@engraft/refunc";
3 | import { describe, expect, it } from "vitest";
4 | import * as checkbox from "../lib/index.js";
5 |
6 | const checkboxTool = toolFromModule(checkbox);
7 |
8 | describe('checkbox', () => {
9 | it('output works', () => {
10 | const memory = new RefuncMemory();
11 | [true, false].forEach((checked) => {
12 | const {outputP} = checkboxTool.run(memory, {
13 | program: {
14 | toolName: 'checkbox',
15 | checked,
16 | },
17 | varBindings: {},
18 | context: undefined as any, // not needed here
19 | });
20 | expect(EngraftPromise.state(outputP)).toEqual({status: 'fulfilled', value: {value: checked}});
21 | });
22 | })
23 | });
24 |
--------------------------------------------------------------------------------
/packages/tool-checkbox/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-checkbox/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-data-table/README.md:
--------------------------------------------------------------------------------
1 | # @engraft/tool-data-table
2 |
3 | Inspired by https://observablehq.com/@observablehq/data-table-cell.
4 |
--------------------------------------------------------------------------------
/packages/tool-data-table/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-data-table",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Engraft tool to edit table in an interactive table",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "test:watch": "vitest",
11 | "test": "vitest run",
12 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/core-widgets": "^0.0.9",
21 | "@engraft/shared": "^0.0.9",
22 | "@engraft/toolkit": "^0.0.9",
23 | "@reach/menu-button": "^0.18.0"
24 | },
25 | "peerDependencies": {
26 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc",
27 | "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/tool-data-table/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-data-table/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-example-datasets/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-example-datasets",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Observable example datasets, as an Engraft tool",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/toolkit": "^0.0.9",
21 | "d3-dsv": "^3.0.1"
22 | },
23 | "peerDependencies": {
24 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/tool-example-datasets/src/datasets.ts:
--------------------------------------------------------------------------------
1 | import { aapl } from "./datasets/aapl.js";
2 | import { alphabet } from "./datasets/alphabet.js";
3 | import { cars } from "./datasets/cars.js";
4 | import { citywages } from "./datasets/citywages.js";
5 | import { diamonds } from "./datasets/diamonds.js";
6 | import { flare } from "./datasets/flare.js";
7 | import { industries } from "./datasets/industries.js";
8 | import { miserables } from "./datasets/miserables.js";
9 | import { olympians } from "./datasets/olympians.js";
10 | import { penguins } from "./datasets/penguins.js";
11 | import { weather } from "./datasets/weather.js";
12 | import * as d3dsv from "d3-dsv";
13 |
14 | function processDataset(dataset: { type: string, text: string }): any {
15 | if (dataset.type === "csv") {
16 | return d3dsv.csvParse(dataset.text, d3dsv.autoType);
17 | } else if (dataset.type === "json") {
18 | return JSON.parse(dataset.text);
19 | } else {
20 | throw new Error(`Unknown dataset type: ${dataset.type}`);
21 | }
22 | };
23 |
24 | export const datasets = {
25 | aapl: processDataset(aapl),
26 | alphabet: processDataset(alphabet),
27 | cars: processDataset(cars),
28 | citywages: processDataset(citywages),
29 | diamonds: processDataset(diamonds),
30 | flare: processDataset(flare),
31 | industries: processDataset(industries),
32 | miserables: processDataset(miserables),
33 | olympians: processDataset(olympians),
34 | penguins: processDataset(penguins),
35 | weather: processDataset(weather),
36 | };
37 |
--------------------------------------------------------------------------------
/packages/tool-example-datasets/src/datasets/alphabet.ts:
--------------------------------------------------------------------------------
1 | export const alphabet = {
2 | type: "csv",
3 | text: "letter,frequency\nE,0.12702\nT,0.09056\nA,0.08167\nO,0.07507\nI,0.06966\nN,0.06749\nS,0.06327\nH,0.06094\nR,0.05987\nD,0.04253\nL,0.04025\nC,0.02782\nU,0.02758\nM,0.02406\nW,0.0236\nF,0.02288\nG,0.02015\nY,0.01974\nP,0.01929\nB,0.01492\nV,0.00978\nK,0.00772\nJ,0.00153\nX,0.0015\nQ,0.00095\nZ,0.00074\n",
4 | };
5 |
--------------------------------------------------------------------------------
/packages/tool-example-datasets/src/index.tsx:
--------------------------------------------------------------------------------
1 | import { defineSimpleTool } from "@engraft/toolkit";
2 | import { datasets } from "./datasets.js";
3 |
4 | export default defineSimpleTool({
5 | name: 'example-datasets',
6 | fields: {
7 | datasetName: 'aapl' as keyof typeof datasets,
8 | },
9 | subTools: [],
10 | compute: ({ fields }) => {
11 | return datasets[fields.datasetName];
12 | },
13 | render: ({ fields: { datasetName }, fieldsUP }) => (
14 |
23 | ),
24 | })
25 |
--------------------------------------------------------------------------------
/packages/tool-example-datasets/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-example-datasets/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-extractor/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-extractor",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Engraft tool for extracting patterns of data from JSON structures",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "test:watch": "vitest",
11 | "test": "vitest run",
12 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/shared": "^0.0.9",
21 | "@engraft/toolkit": "^0.0.9"
22 | },
23 | "peerDependencies": {
24 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc",
25 | "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/tool-extractor/src/internmap.d.ts:
--------------------------------------------------------------------------------
1 | export class InternMap extends Map {
2 | constructor(entries?: readonly (readonly [K, V])[] | null, key?: (key: string) => any)
3 | }
4 |
--------------------------------------------------------------------------------
/packages/tool-extractor/src/mapUpdate.tsx:
--------------------------------------------------------------------------------
1 | export function mapUpdate(map: Map, key: K, func: (oldV: V | undefined) => V | undefined) {
2 | const oldV = map.get(key);
3 | const newV = func(oldV);
4 | if (newV !== undefined) {
5 | map.set(key, newV);
6 | } else if (oldV !== undefined) {
7 | map.delete(key);
8 | }
9 | }
--------------------------------------------------------------------------------
/packages/tool-extractor/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-extractor/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-function/README.md:
--------------------------------------------------------------------------------
1 | # @engraft/tool-function
2 |
3 | TODO
4 |
--------------------------------------------------------------------------------
/packages/tool-function/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-function",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Engraft tool to define a reusable function, working off of concrete examples",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "test:watch": "vitest",
11 | "test": "vitest run",
12 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/shared": "^0.0.9",
21 | "@engraft/toolkit": "^0.0.9"
22 | },
23 | "peerDependencies": {
24 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc",
25 | "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/tool-function/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-function/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-gadget/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-gadget",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Engraft tool that allows defining a simple tool and then using it",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/toolkit": "^0.0.9"
21 | },
22 | "peerDependencies": {
23 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/tool-gadget/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./gadget-definer.js";
2 | export * from "./gadget-user.js";
3 |
--------------------------------------------------------------------------------
/packages/tool-gadget/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-gadget/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-geom/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-geom",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "A simple geometric scripter for Engraft",
6 | "scripts": {
7 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
8 | "test:watch": "vitest",
9 | "test": "vitest run",
10 | "depcheck": "node ../../scripts/our-depcheck.cjs",
11 | "lint": "eslint --max-warnings=0 .",
12 | "tsc": "tsc"
13 | },
14 | "main": "lib/index.js",
15 | "type": "module",
16 | "dependencies": {
17 | "@engraft/hostkit": "^0.0.9",
18 | "@engraft/shared": "^0.0.9",
19 | "@engraft/toolkit": "^0.0.9"
20 | },
21 | "peerDependencies": {
22 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc",
23 | "react-dom": "^18.2.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/tool-geom/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-geom/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-hider/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-hider",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Engraft tool that can hide its child",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/tool-checkbox": "^0.0.9",
21 | "@engraft/toolkit": "^0.0.9"
22 | },
23 | "peerDependencies": {
24 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/tool-hider/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-hider/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-map/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-map",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Engraft tool for mapping an array or object",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/shared": "^0.0.9",
21 | "@engraft/toolkit": "^0.0.9",
22 | "lodash": "^4.17.21",
23 | "react-dom": "^18.2.0"
24 | },
25 | "peerDependencies": {
26 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/tool-map/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-map/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-markdown/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-markdown",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Engraft tool for Markdown (MDX, actually)",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@codemirror/lang-markdown": "^6.1.1",
21 | "@engraft/codemirror-helpers": "^0.0.9",
22 | "@engraft/toolkit": "^0.0.9",
23 | "@mdx-js/mdx": "^2.3.0"
24 | },
25 | "peerDependencies": {
26 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/tool-markdown/src/jsx-runtime.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'react/jsx-runtime';
2 |
--------------------------------------------------------------------------------
/packages/tool-markdown/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-markdown/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-notebook-canvas/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-notebook-canvas",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "A spatial notebook canvas for Engraft",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/shared": "^0.0.9",
21 | "@engraft/toolkit": "^0.0.9",
22 | "lodash": "^4.17.21"
23 | },
24 | "peerDependencies": {
25 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc",
26 | "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/tool-notebook-canvas/src/noodle-canvas/model.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from "react";
2 |
3 | export type PaneGeo = {
4 | x: number,
5 | y: number,
6 | width: number,
7 | height: number,
8 | }
9 |
10 | export type Pane = {
11 | id: string,
12 | geo: PaneGeo,
13 | children: (props: {onMouseDownDragPane: (startEvent: React.MouseEvent) => void}) => ReactNode,
14 | transparent?: boolean,
15 | }
16 |
17 | export function roundTo(n: number, step: number) {
18 | return Math.round(n / step) * step;
19 | }
20 |
--------------------------------------------------------------------------------
/packages/tool-notebook-canvas/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-notebook-canvas/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-notebook/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-notebook",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "The famous Engraft notebook component",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/shared": "^0.0.9",
21 | "@engraft/toolkit": "^0.0.9",
22 | "lodash": "^4.17.21",
23 | "react-merge-refs": "^2.0.1"
24 | },
25 | "peerDependencies": {
26 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/tool-notebook/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-notebook/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-python/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-python",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Engraft tool for Python code (via Pyodide)",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@codemirror/lang-python": "^6.1.2",
21 | "@engraft/codemirror-helpers": "^0.0.9",
22 | "@engraft/pyodide": "^0.0.9",
23 | "@engraft/toolkit": "^0.0.9"
24 | },
25 | "peerDependencies": {
26 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/tool-python/src/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'https://*';
2 |
--------------------------------------------------------------------------------
/packages/tool-python/test/index.test.tsx:
--------------------------------------------------------------------------------
1 | import { runTool, toolFromModule } from "@engraft/core";
2 | import { RefuncMemory } from "@engraft/refunc";
3 | import { empty } from "@engraft/shared/lib/noOp.js";
4 | import { describe, expect, it } from "vitest";
5 | import * as Python from "../lib/index.js";
6 | import { makeTestingContext } from "@engraft/testing-setup";
7 |
8 | // @vitest-environment happy-dom
9 |
10 | const context = makeTestingContext();
11 | context.dispatcher.registerTool(toolFromModule(Python));
12 |
13 | describe('python', () => {
14 | it('basically works', async () => {
15 | const memory = new RefuncMemory();
16 |
17 | let program: Python.Program = {
18 | toolName: 'python',
19 | code: '1 if True else 2',
20 | subPrograms: {},
21 | }
22 |
23 | const output = await runTool(memory, {
24 | program,
25 | varBindings: empty,
26 | context,
27 | }).outputP;
28 |
29 | expect(output).toEqual({ value: 1 });
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/packages/tool-python/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-python/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-slot/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-slot",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "The famous Engraft slot component",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@babel/plugin-proposal-do-expressions": "^7.18.6",
21 | "@babel/standalone": "^7.20.12",
22 | "@babel/types": "^7.21.2",
23 | "@codemirror/lang-javascript": "^6.1.2",
24 | "@codemirror/state": "^6.1.1",
25 | "@codemirror/view": "^6.2.0",
26 | "@engraft/codemirror-helpers": "^0.0.9",
27 | "@engraft/shared": "^0.0.9",
28 | "@engraft/toolkit": "^0.0.9",
29 | "@googlemaps/react-wrapper": "^1.1.35",
30 | "d3-dsv": "^3.0.1",
31 | "immutability-helper": "^3.1.1",
32 | "lodash": "^4.17.21",
33 | "object-inspect": "^1.12.3",
34 | "react-dom": "^18.2.0",
35 | "seedrandom": "^3.0.5"
36 | },
37 | "peerDependencies": {
38 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
39 | },
40 | "devDependencies": {
41 | "@babel/core": "^7.20.12",
42 | "@types/babel__core": "7.20.0",
43 | "@types/babel__standalone": "7.1.4"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/packages/tool-slot/src/babel_plugin-proposal-do-expressions.d.ts:
--------------------------------------------------------------------------------
1 | declare module "@babel/plugin-proposal-do-expressions" {
2 | import type { PluginItem } from "@babel/core";
3 | const plugin: PluginItem;
4 | export = plugin;
5 | };
6 |
--------------------------------------------------------------------------------
/packages/tool-slot/src/globals/createElementFrom.ts:
--------------------------------------------------------------------------------
1 | import { ReactElement } from "react";
2 | import ReactDOM from "react-dom";
3 |
4 | export function createElementFromHTML(htmlString: string) {
5 | var div = document.createElement('div');
6 | div.innerHTML = htmlString.trim();
7 | return div.firstChild;
8 | }
9 |
10 | export function createElementFromReact(element: ReactElement): Promise {
11 | var container = document.createElement('div');
12 | return new Promise((resolve) => {
13 | // TODO: still using legacy (pre v18) React render
14 | ReactDOM.render(element, container, () => {
15 | resolve(container.firstChild);
16 | });
17 | })
18 | }
19 |
--------------------------------------------------------------------------------
/packages/tool-slot/src/globals/rand.ts:
--------------------------------------------------------------------------------
1 | import seedrandom from "seedrandom";
2 |
3 | export function makeRand(): (...args: unknown[]) => number {
4 | const generators: { [key: string]: seedrandom.PRNG } = {};
5 | return (...args: any[]) => {
6 | const key = JSON.stringify(args);
7 | if (!generators[key]) {
8 | generators[key] = seedrandom(key);
9 | }
10 | return generators[key]();
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/tool-slot/tsconfig.build copy.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-slot/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/tool-slot/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "test", "../../types/lib.d.ts" ]
4 | }
5 |
--------------------------------------------------------------------------------
/packages/tool-synthesizer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@engraft/tool-synthesizer",
3 | "version": "0.0.9",
4 | "license": "MIT",
5 | "description": "Toy programming-by-example synthesizer for Engraft",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib",
11 | "test:watch": "vitest",
12 | "test": "vitest run",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 .",
15 | "tsc": "tsc"
16 | },
17 | "main": "lib/index.js",
18 | "type": "module",
19 | "dependencies": {
20 | "@engraft/codemirror-helpers": "^0.0.9",
21 | "@engraft/shared": "^0.0.9",
22 | "@engraft/toolkit": "^0.0.9",
23 | "lodash": "^4.17.21"
24 | },
25 | "peerDependencies": {
26 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc",
27 | "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/tool-synthesizer/src/Task.tsx:
--------------------------------------------------------------------------------
1 | interface TaskOptions
;
9 | });
10 |
--------------------------------------------------------------------------------
/templates/app/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { createRoot } from "react-dom/client";
3 | import { HashRouter, Route, Routes } from "react-router-dom";
4 | import { App } from "./App.js";
5 |
6 | const root = createRoot(document.getElementById('root')!);
7 |
8 | root.render(
9 |
10 |
11 |
12 | }/>
13 |
14 |
15 | ,
16 | );
17 |
--------------------------------------------------------------------------------
/templates/app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "types": [
6 | "vite/client"
7 | ]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/templates/lib/README.md:
--------------------------------------------------------------------------------
1 | # CHANGEME
2 |
3 | All about the lib!
4 |
--------------------------------------------------------------------------------
/templates/lib/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "CHANGEME: package name",
3 | "version": "0.0.1",
4 | "license": "MIT",
5 | "description": "CHANGEME: package description",
6 | "publishConfig": {
7 | "access": "public"
8 | },
9 | "scripts": {
10 | "test:watch": "vitest",
11 | "test": "vitest run",
12 | "build-lib": "tsc && cpy 'src/**/*.css' lib",
13 | "depcheck": "node ../../scripts/our-depcheck.cjs",
14 | "lint": "eslint --max-warnings=0 ."
15 | },
16 | "main": "lib/index.js",
17 | "type": "module",
18 | "dependencies": {
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/templates/lib/src/index.ts:
--------------------------------------------------------------------------------
1 | export const CHANGEME = 10;
2 |
--------------------------------------------------------------------------------
/templates/lib/test/index.test.ts:
--------------------------------------------------------------------------------
1 | import { describe, expect, it } from "vitest";
2 | import { CHANGEME } from "../dist/index.js";
3 |
4 | describe('CHANGEME', () => {
5 | it('CHANGEME', () => {
6 | expect(CHANGEME).toBe(10);
7 | });
8 | });
9 |
--------------------------------------------------------------------------------
/templates/lib/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "include": [ "src", "../../types/lib.d.ts" ],
4 | "compilerOptions": {
5 | "noEmit": false,
6 | "outDir": "lib"
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "NodeNext",
17 | "moduleResolution": "NodeNext",
18 | "resolveJsonModule": true,
19 | "jsx": "react-jsx",
20 | "sourceMap": true,
21 | "noEmit": true,
22 | "declaration": true,
23 | "declarationMap": true,
24 | "incremental": true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/types/lib.d.ts:
--------------------------------------------------------------------------------
1 | // ambient declaration file for our css-to-js system
2 |
3 | declare module '*.css.js' {
4 | const src: string
5 | export default src
6 | }
7 |
8 | declare module '*.css.cjs' {
9 | // TODO: this one's only for voyager; please get rid of it
10 | const src: string
11 | export default src
12 | }
13 |
--------------------------------------------------------------------------------
/website/CNAME:
--------------------------------------------------------------------------------
1 | engraft.dev
2 |
--------------------------------------------------------------------------------
/website/engraft-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/engraft-architecture.png
--------------------------------------------------------------------------------
/website/engraft-uist-2023.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/engraft-uist-2023.pdf
--------------------------------------------------------------------------------
/website/engraft-uist-5min.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/engraft-uist-5min.mp4
--------------------------------------------------------------------------------
/website/et-book/et-book-bold-line-figures/et-book-bold-line-figures.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-bold-line-figures/et-book-bold-line-figures.eot
--------------------------------------------------------------------------------
/website/et-book/et-book-bold-line-figures/et-book-bold-line-figures.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-bold-line-figures/et-book-bold-line-figures.ttf
--------------------------------------------------------------------------------
/website/et-book/et-book-bold-line-figures/et-book-bold-line-figures.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-bold-line-figures/et-book-bold-line-figures.woff
--------------------------------------------------------------------------------
/website/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.eot
--------------------------------------------------------------------------------
/website/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.ttf
--------------------------------------------------------------------------------
/website/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-display-italic-old-style-figures/et-book-display-italic-old-style-figures.woff
--------------------------------------------------------------------------------
/website/et-book/et-book-roman-line-figures/et-book-roman-line-figures.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-roman-line-figures/et-book-roman-line-figures.eot
--------------------------------------------------------------------------------
/website/et-book/et-book-roman-line-figures/et-book-roman-line-figures.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-roman-line-figures/et-book-roman-line-figures.ttf
--------------------------------------------------------------------------------
/website/et-book/et-book-roman-line-figures/et-book-roman-line-figures.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-roman-line-figures/et-book-roman-line-figures.woff
--------------------------------------------------------------------------------
/website/et-book/et-book-roman-old-style-figures/et-book-roman-old-style-figures.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-roman-old-style-figures/et-book-roman-old-style-figures.eot
--------------------------------------------------------------------------------
/website/et-book/et-book-roman-old-style-figures/et-book-roman-old-style-figures.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-roman-old-style-figures/et-book-roman-old-style-figures.ttf
--------------------------------------------------------------------------------
/website/et-book/et-book-roman-old-style-figures/et-book-roman-old-style-figures.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-roman-old-style-figures/et-book-roman-old-style-figures.woff
--------------------------------------------------------------------------------
/website/et-book/et-book-semi-bold-old-style-figures/et-book-semi-bold-old-style-figures.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-semi-bold-old-style-figures/et-book-semi-bold-old-style-figures.eot
--------------------------------------------------------------------------------
/website/et-book/et-book-semi-bold-old-style-figures/et-book-semi-bold-old-style-figures.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-semi-bold-old-style-figures/et-book-semi-bold-old-style-figures.ttf
--------------------------------------------------------------------------------
/website/et-book/et-book-semi-bold-old-style-figures/et-book-semi-bold-old-style-figures.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/et-book/et-book-semi-bold-old-style-figures/et-book-semi-bold-old-style-figures.woff
--------------------------------------------------------------------------------
/website/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/engraftdev/engraft/033afcb275e47d586ada0a6171e25790ed1a2cbd/website/logo.png
--------------------------------------------------------------------------------