├── .eslintrc.cjs ├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── LICENSE ├── README.md ├── lerna.json ├── nx.json ├── package.json ├── packages ├── basic-setup │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── cli │ ├── README.md │ ├── css-loader.mjs │ ├── engraft │ ├── index.html │ ├── package.json │ ├── src │ │ ├── backend │ │ │ ├── env.ts │ │ │ └── index.ts │ │ ├── frontend │ │ │ ├── App.css │ │ │ ├── App.tsx │ │ │ └── index.tsx │ │ ├── run.ts │ │ └── shared.ts │ ├── test │ │ └── run.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── codemirror-helpers │ ├── README.md │ ├── package.json │ ├── src │ │ ├── CodeMirror.tsx │ │ ├── FancyCodeEditor.tsx │ │ ├── PortalWidget.tsx │ │ ├── codeMirrorStuff.tsx │ │ ├── embedsExtension.tsx │ │ ├── index.ts │ │ ├── refs.ts │ │ └── useInterval.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── core-widgets │ ├── README.md │ ├── package.json │ ├── src │ │ ├── IsolateStyles.tsx │ │ ├── MyContextMenu.css │ │ ├── MyContextMenu.tsx │ │ ├── ScrollShadow.tsx │ │ ├── Value.tsx │ │ ├── ValueEditable.tsx │ │ ├── Vars.tsx │ │ ├── index.ts │ │ └── root.css │ ├── tsconfig.build.json │ └── tsconfig.json ├── core │ ├── package.json │ ├── src │ │ ├── EngraftPromise-refunc.ts │ │ ├── EngraftPromise.ts │ │ ├── ToolResultWithScope.ts │ │ ├── context.ts │ │ ├── core.ts │ │ ├── index.ts │ │ ├── randomId.d.ts │ │ ├── randomId.ts │ │ ├── runTool.tsx │ │ ├── test-utils.ts │ │ └── toolFromModule.ts │ ├── test │ │ └── EngraftPromise.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── eslint-plugin-refunc-hooks │ ├── package.json │ ├── src │ │ ├── ExhaustiveDeps.js │ │ └── index.js │ └── test │ │ └── ExhaustiveDeps.test.ts ├── extension-observable │ ├── README.md │ ├── package.json │ ├── public │ │ ├── content_script.js │ │ └── manifest.json │ ├── src │ │ ├── injected_script.ts │ │ └── util │ │ │ ├── ASTLibrary.ts │ │ │ └── version.ts │ ├── test │ │ └── ASTLibrary.test.ts │ └── vite.config.ts ├── fancy-setup │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── graft-garden │ ├── index.html │ ├── package.json │ ├── src │ │ ├── EditPatch.tsx │ │ ├── Patches.tsx │ │ ├── Root.tsx │ │ ├── StyledFirebaseAuth.tsx │ │ ├── TinkerPatch.tsx │ │ ├── ViewPatch.tsx │ │ ├── db.tsx │ │ ├── index.tsx │ │ ├── react-firebase-hooks.d.ts │ │ ├── useDocumentDataAndUpdater.tsx │ │ ├── usePatchState.tsx │ │ └── util.tsx │ └── tsconfig.json ├── host-observable │ ├── README.md │ ├── package.json │ ├── src │ │ ├── ExtensionBanner.tsx │ │ ├── ObservableInspector.css │ │ ├── ObservableInspector.d.ts │ │ ├── ObservableInspector.tsx │ │ ├── index.css │ │ └── index.tsx │ ├── tsconfig.json │ └── vite.config.ts ├── host-python │ ├── .gitignore │ ├── engraft.py │ ├── package.json │ └── tests │ │ ├── example_response.json │ │ ├── mixed_type.json │ │ ├── nd_array.json │ │ ├── nd_array_multidimensional.json │ │ ├── test_basic.py │ │ └── wikipedia.json ├── hostkit │ ├── package.json │ ├── src │ │ ├── ToolWithView.tsx │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── original-tools │ ├── package.json │ ├── src │ │ ├── color │ │ │ ├── TODO.md │ │ │ └── index.tsx │ │ ├── file │ │ │ └── index.tsx │ │ ├── formatter │ │ │ ├── builtin.css │ │ │ ├── elements-and-nodes.tsx │ │ │ ├── id.ts │ │ │ └── index.tsx │ │ ├── index.ts │ │ ├── not-found │ │ │ └── index.tsx │ │ ├── npm │ │ │ └── index.tsx │ │ ├── request │ │ │ ├── RowToCol.tsx │ │ │ └── index.tsx │ │ ├── simple-chart │ │ │ ├── icons.tsx │ │ │ └── index.tsx │ │ ├── simulation │ │ │ ├── SimSlider.tsx │ │ │ ├── TODO.md │ │ │ └── index.tsx │ │ ├── slider │ │ │ └── index.tsx │ │ ├── test-array │ │ │ └── index.tsx │ │ ├── test-delay │ │ │ └── index.tsx │ │ ├── test-seeing-double │ │ │ └── index.tsx │ │ ├── test-show-program │ │ │ ├── TODO.md │ │ │ └── index.tsx │ │ └── test-text-latency │ │ │ └── index.tsx │ ├── test │ │ ├── simulation │ │ │ └── index.test.ts │ │ └── test-array │ │ │ └── index.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── pyodide │ ├── package.json │ ├── src │ │ ├── index.d.ts │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── react │ ├── package.json │ ├── src │ │ ├── EngraftPromise-react.ts │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── refunc-react │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── useRefunction.ts │ ├── test │ │ └── useRefunction.test.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── refunc │ ├── README.md │ ├── package.json │ ├── src │ │ ├── hookMemo.ts │ │ ├── hooks-debug.ts │ │ ├── hooks.ts │ │ ├── index.ts │ │ ├── memoize.ts │ │ └── refunc.ts │ ├── test │ │ ├── hookMemo.test.ts │ │ ├── hooks.test.ts │ │ └── memoize.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── shared │ ├── package.json │ ├── src │ │ ├── ControlledTextInput.tsx │ │ ├── DOM.tsx │ │ ├── Diagram.jsx │ │ ├── ErrorBoundary.tsx │ │ ├── Graphic.tsx │ │ ├── OrError.ts │ │ ├── ShadowDOM.tsx │ │ ├── Updater.ts │ │ ├── Use.tsx │ │ ├── assert.ts │ │ ├── cache.ts │ │ ├── compare.ts │ │ ├── compile.ts │ │ ├── count.ts │ │ ├── drag.ts │ │ ├── eq.ts │ │ ├── geom.ts │ │ ├── hasProperty.ts │ │ ├── isArray.ts │ │ ├── isObject.ts │ │ ├── isoformat.ts │ │ ├── noOp.ts │ │ ├── normalizeIndent.ts │ │ ├── runtimeObjectId.ts │ │ ├── saveFile.ts │ │ ├── sets.ts │ │ ├── unusedLabel.ts │ │ ├── useContextMenu.tsx │ │ ├── useDedupe.tsx │ │ ├── useEventListener.tsx │ │ ├── useHover.tsx │ │ ├── useKeyHeld.tsx │ │ ├── useLocalStorage.ts │ │ ├── useRefForCallback.tsx │ │ └── useSize.tsx │ ├── test │ │ ├── eq.test.ts │ │ ├── isObject.test.ts │ │ ├── normalizeIndent.test.ts │ │ └── useLocalStorage.test.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── testbed │ ├── index.html │ ├── package.json │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── examples │ │ │ ├── 2023-01-21-simulation-bouncing.json │ │ │ ├── 2023-01-26-function-map.json │ │ │ ├── 2023-01-26-refunc.json │ │ │ ├── 2023-01-27-meta.json │ │ │ ├── 2023-01-31-test-delay.json │ │ │ ├── 2023-02-18-simple-chart.json │ │ │ ├── 2023-03-03-gadgets.json │ │ │ ├── 2023-03-04-silly-sliders.json │ │ │ ├── 2023-03-15-wikipedia-2.json │ │ │ ├── 2023-03-15-wikipedia.json │ │ │ ├── 2023-03-24-simulation-geom.json │ │ │ ├── 2023-03-28-wikipedia-canvas.json │ │ │ ├── 2023-04-26-python.json │ │ │ ├── fig-bouncing.json │ │ │ ├── fig-cuttle.json │ │ │ ├── fig-map-tool.json │ │ │ ├── index.ts │ │ │ ├── menagerie-checkbox.json │ │ │ ├── menagerie-color.json │ │ │ ├── menagerie-example-datasets.json │ │ │ ├── menagerie-file.json │ │ │ ├── menagerie-function.json │ │ │ ├── menagerie-gadgets.json │ │ │ ├── menagerie-npm.json │ │ │ ├── menagerie-simple-chart.json │ │ │ ├── menagerie-slider.json │ │ │ └── menagerie-synthesizer.json │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── testing-setup │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── testing-known-output.tsx │ │ └── testing-refs-func.tsx │ ├── test │ │ └── testing-known-output.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-checkbox │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── test │ │ └── index.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-data-table │ ├── README.md │ ├── package.json │ ├── src │ │ ├── data-frame.ts │ │ ├── index.tsx │ │ ├── style.css │ │ └── transforms.ts │ ├── test │ │ ├── data-frame.test.ts │ │ └── transforms.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-example-datasets │ ├── package.json │ ├── src │ │ ├── datasets.ts │ │ ├── datasets │ │ │ ├── aapl.ts │ │ │ ├── alphabet.ts │ │ │ ├── cars.ts │ │ │ ├── citywages.ts │ │ │ ├── diamonds.ts │ │ │ ├── flare.ts │ │ │ ├── industries.ts │ │ │ ├── miserables.ts │ │ │ ├── olympians.ts │ │ │ ├── penguins.ts │ │ │ └── weather.ts │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-extractor │ ├── package.json │ ├── src │ │ ├── index.tsx │ │ ├── internmap.d.ts │ │ ├── internmap.js │ │ ├── mapUpdate.tsx │ │ └── patterns.ts │ ├── test │ │ └── index.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-function │ ├── README.md │ ├── package.json │ ├── src │ │ ├── closure.ts │ │ └── index.tsx │ ├── test │ │ ├── closure.test.ts │ │ └── index.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-gadget │ ├── package.json │ ├── src │ │ ├── core.ts │ │ ├── gadget-definer.tsx │ │ ├── gadget-user.tsx │ │ └── index.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-geom │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-hider │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-map │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── test │ │ └── index.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-markdown │ ├── package.json │ ├── src │ │ ├── index.tsx │ │ └── jsx-runtime.d.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-notebook-canvas │ ├── package.json │ ├── src │ │ ├── index.tsx │ │ └── noodle-canvas │ │ │ ├── NoodleCanvas.tsx │ │ │ ├── PaneResizers.tsx │ │ │ ├── PaneView.tsx │ │ │ └── model.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-notebook │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── test │ │ └── index.test.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-python │ ├── package.json │ ├── src │ │ ├── index.d.ts │ │ └── index.tsx │ ├── test │ │ └── index.test.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-slot │ ├── package.json │ ├── src │ │ ├── ToolFrame.tsx │ │ ├── ToolInspectorWindow.tsx │ │ ├── WindowPortal.tsx │ │ ├── babel_plugin-proposal-do-expressions.d.ts │ │ ├── globals │ │ │ ├── GoogleMap.tsx │ │ │ ├── createElementFrom.ts │ │ │ ├── index.tsx │ │ │ └── rand.ts │ │ └── index.tsx │ ├── test │ │ └── index.test.ts │ ├── tsconfig.build copy.json │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-synthesizer │ ├── package.json │ ├── src │ │ ├── Task.tsx │ │ ├── index.tsx │ │ └── synthesizer.ts │ ├── test │ │ └── synthesizer.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-test-count-runs │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-text │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-toy-adder-simple │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-toy-adder-vanilla │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-toy-adder │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-toy-hello-world-vanilla │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-toy-hello-world │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-value │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-vite-lib │ ├── package.json │ ├── src │ │ └── index.tsx │ ├── tsconfig.build.json │ └── tsconfig.json ├── tool-voyager │ ├── package.json │ ├── src │ │ ├── index.tsx │ │ └── voyager-style.css │ ├── tsconfig.build.json │ └── tsconfig.json ├── toolkit │ ├── package.json │ ├── src │ │ ├── CommonWidth.tsx │ │ ├── cellNetwork.tsx │ │ ├── index.ts │ │ ├── input.tsx │ │ ├── simple-tool.tsx │ │ └── toposort.ts │ ├── test │ │ ├── cellNetwork.test.ts │ │ ├── simple-tool.test.tsx │ │ └── toposort.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── update-proxy │ ├── package.json │ ├── src │ │ └── index.ts │ ├── test │ │ └── index.test.ts │ ├── tsconfig.build.json │ └── tsconfig.json ├── use-engraft-demo-js │ ├── index.html │ ├── package.json │ ├── src │ │ ├── App.css │ │ ├── App.jsx │ │ ├── extractSynonymsProgram.json │ │ └── index.jsx │ └── tsconfig.json ├── use-engraft-demo │ ├── index.html │ ├── package.json │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ ├── extractSynonymsProgram.json │ │ └── index.tsx │ └── tsconfig.json ├── use-engraft │ ├── package.json │ ├── src │ │ ├── index.css │ │ └── index.tsx │ ├── test │ │ └── index.test.tsx │ ├── tsconfig.build.json │ └── tsconfig.json └── vendor-voyager │ ├── README.md │ ├── package.json │ └── src │ ├── lib-voyager.cjs │ └── lib-voyager.d.cts ├── scripts ├── css-to-js.cjs └── our-depcheck.cjs ├── templates ├── app │ ├── README.md │ ├── index.html │ ├── package.json │ ├── src │ │ ├── App.css │ │ ├── App.tsx │ │ └── index.tsx │ └── tsconfig.json └── lib │ ├── README.md │ ├── package.json │ ├── src │ └── index.ts │ ├── test │ └── index.test.ts │ └── tsconfig.json ├── tsconfig.json ├── types └── lib.d.ts ├── vite.config.ts ├── website ├── CNAME ├── engraft-architecture.png ├── engraft-uist-2023.pdf ├── engraft-uist-5min.mp4 ├── et-book │ ├── et-book-bold-line-figures │ │ ├── et-book-bold-line-figures.eot │ │ ├── et-book-bold-line-figures.svg │ │ ├── et-book-bold-line-figures.ttf │ │ └── et-book-bold-line-figures.woff │ ├── et-book-display-italic-old-style-figures │ │ ├── et-book-display-italic-old-style-figures.eot │ │ ├── et-book-display-italic-old-style-figures.svg │ │ ├── et-book-display-italic-old-style-figures.ttf │ │ └── et-book-display-italic-old-style-figures.woff │ ├── et-book-roman-line-figures │ │ ├── et-book-roman-line-figures.eot │ │ ├── et-book-roman-line-figures.svg │ │ ├── et-book-roman-line-figures.ttf │ │ └── et-book-roman-line-figures.woff │ ├── et-book-roman-old-style-figures │ │ ├── et-book-roman-old-style-figures.eot │ │ ├── et-book-roman-old-style-figures.svg │ │ ├── et-book-roman-old-style-figures.ttf │ │ └── et-book-roman-old-style-figures.woff │ └── et-book-semi-bold-old-style-figures │ │ ├── et-book-semi-bold-old-style-figures.eot │ │ ├── et-book-semi-bold-old-style-figures.svg │ │ ├── et-book-semi-bold-old-style-figures.ttf │ │ └── et-book-semi-bold-old-style-figures.woff ├── index.html ├── logo.png └── tufte.css └── yarn.lock /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('eslint').Linter.Config} */ 2 | module.exports = { 3 | root: true, 4 | extends: [ 5 | "react-app" 6 | ], 7 | plugins: [ 8 | "@engraft/refunc-hooks" 9 | ], 10 | settings: { 11 | 'import/resolver': { 12 | typescript: true 13 | } 14 | }, 15 | ignorePatterns: [ 16 | "dist", 17 | "lib", 18 | ], 19 | rules: { 20 | "@typescript-eslint/no-unused-vars": [ 21 | "warn", 22 | { 23 | args: "none", 24 | ignoreRestSiblings: true, 25 | argsIgnorePattern: "^_", 26 | varsIgnorePattern: "^_" 27 | } 28 | ], 29 | "@engraft/refunc-hooks/exhaustive-deps": "warn", 30 | "import/no-restricted-paths": [ 31 | "error", 32 | { 33 | zones: [ 34 | { 35 | target: "./test", 36 | from: "./src", 37 | } 38 | ] 39 | } 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | /.pnp 6 | .pnp.js 7 | /.yalc 8 | 9 | # testing 10 | /coverage 11 | 12 | # production 13 | dist 14 | lib 15 | 16 | # misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | lerna-debug.log* 27 | 28 | tsconfig.tsbuildinfo 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023, Joshua Horowitz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice (including the next 13 | paragraph) shall be included in all copies or substantial portions of the 14 | Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 3 | "version": "0.0.9" 4 | } 5 | -------------------------------------------------------------------------------- /packages/basic-setup/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/basic-setup", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Basic setup for Engraft, with tools built by the Engraft team.", 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": "^0.0.9", 21 | "@engraft/original-tools": "^0.0.9", 22 | "@engraft/tool-checkbox": "^0.0.9", 23 | "@engraft/tool-data-table": "^0.0.9", 24 | "@engraft/tool-extractor": "^0.0.9", 25 | "@engraft/tool-function": "^0.0.9", 26 | "@engraft/tool-gadget": "^0.0.9", 27 | "@engraft/tool-hider": "^0.0.9", 28 | "@engraft/tool-map": "^0.0.9", 29 | "@engraft/tool-markdown": "^0.0.9", 30 | "@engraft/tool-notebook": "^0.0.9", 31 | "@engraft/tool-notebook-canvas": "^0.0.9", 32 | "@engraft/tool-slot": "^0.0.9", 33 | "@engraft/tool-synthesizer": "^0.0.9", 34 | "@engraft/tool-test-count-runs": "^0.0.9", 35 | "@engraft/tool-text": "^0.0.9", 36 | "@engraft/tool-toy-adder": "^0.0.9", 37 | "@engraft/tool-toy-adder-simple": "^0.0.9", 38 | "@engraft/tool-toy-adder-vanilla": "^0.0.9", 39 | "@engraft/tool-toy-hello-world": "^0.0.9", 40 | "@engraft/tool-toy-hello-world-vanilla": "^0.0.9", 41 | "@engraft/tool-value": "^0.0.9" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/basic-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/basic-setup/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/cli/README.md: -------------------------------------------------------------------------------- 1 | # Engraft CLI interface 2 | 3 | ## TODO 4 | * This isn't really a library; using a build-lib pattern seems a bit off. 5 | -------------------------------------------------------------------------------- /packages/cli/css-loader.mjs: -------------------------------------------------------------------------------- 1 | import { cwd } from 'node:process'; 2 | import { pathToFileURL } from 'node:url'; 3 | 4 | // see https://nodejs.org/api/esm.html#loaders 5 | 6 | const baseURL = pathToFileURL(`${cwd()}/`).href; 7 | 8 | const extensionsRegex = /\.css\?inline$/; 9 | 10 | export async function resolve(specifier, context, nextResolve) { 11 | if (extensionsRegex.test(specifier)) { 12 | const { parentURL = baseURL } = context; 13 | 14 | // Node.js normally errors on unknown file extensions, so return a URL for 15 | // specifiers ending in the file extensions. 16 | return { 17 | shortCircuit: true, 18 | url: new URL(specifier, parentURL).href, 19 | }; 20 | } 21 | 22 | // Let Node.js handle all other specifiers. 23 | return nextResolve(specifier); 24 | } 25 | 26 | export async function load(url, context, nextLoad) { 27 | if (extensionsRegex.test(url)) { 28 | return { 29 | format: 'module', 30 | shortCircuit: true, 31 | source: `export default 'style time';`, 32 | }; 33 | } 34 | 35 | // Defer to the next hook in the chain. 36 | return nextLoad(url); 37 | } 38 | -------------------------------------------------------------------------------- /packages/cli/engraft: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | node lib/run.js "$@" 4 | -------------------------------------------------------------------------------- /packages/cli/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Engraft CLI 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/cli", 3 | "version": "0.0.9", 4 | "description": "Command-line tool to run Engraft tools", 5 | "bin": { 6 | "engraft": "./lib/run.js" 7 | }, 8 | "type": "module", 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "scripts": { 13 | "test:watch": "vitest", 14 | "test": "vitest run", 15 | "build-lib": "tsc --project tsconfig.build.json && node ../../scripts/css-to-js.cjs src lib", 16 | "postbuild-lib": "chmod +x lib/run.js", 17 | "depcheck": "node ../../scripts/our-depcheck.cjs", 18 | "lint": "eslint --max-warnings=0 .", 19 | "build-app": "vite build --config ../../vite.config.ts", 20 | "preview": "vite preview --config ../../vite.config.ts", 21 | "dev": "vite --config ../../vite.config.ts", 22 | "tsc": "tsc" 23 | }, 24 | "dependencies": { 25 | "@engraft/fancy-setup": "^0.0.9", 26 | "@engraft/hostkit": "^0.0.9", 27 | "@engraft/pyodide": "^0.0.9", 28 | "@engraft/shared": "^0.0.9", 29 | "@happy-dom/global-registrator": "^8.9.0", 30 | "express": "^4.18.2", 31 | "react": "^18.0.0", 32 | "react-dom": "^18.0.0", 33 | "react-error-boundary": "^3.1.4", 34 | "react-router-dom": "^6.3.0", 35 | "yargs": "^17.7.1" 36 | }, 37 | "devDependencies": { 38 | "@types/express": "^4.17.17", 39 | "@types/tmp": "^0.2.3", 40 | "@types/yargs": "^17.0.22", 41 | "tmp": "^0.2.1", 42 | "vite": "^4.3.2" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/cli/src/backend/env.ts: -------------------------------------------------------------------------------- 1 | 2 | import { GlobalRegistrator } from '@happy-dom/global-registrator'; 3 | 4 | // TODO: ideally there would be a cheaper way to deal with upstream deps 5 | // expecting a browser environment... 6 | GlobalRegistrator.register(); 7 | -------------------------------------------------------------------------------- /packages/cli/src/frontend/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/cli/src/frontend/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 | -------------------------------------------------------------------------------- /packages/cli/src/run.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { spawnSync } from "node:child_process"; 4 | import { fileURLToPath } from 'node:url'; 5 | 6 | // The only thing this file does is set up the loader. See src/backend/index.ts for the real entry point. 7 | 8 | function relative(path: string) { 9 | return fileURLToPath(new URL(path, import.meta.url)); 10 | } 11 | 12 | const spawned = spawnSync( 13 | 'node', 14 | [ 15 | '--no-warnings', 16 | '--experimental-loader', relative('../css-loader.mjs'), 17 | relative('./backend/index.js'), 18 | ...process.argv.slice(2), 19 | ], 20 | { 21 | stdio: 'inherit', 22 | } 23 | ); 24 | 25 | process.exitCode = spawned.status ?? 1; 26 | -------------------------------------------------------------------------------- /packages/cli/test/run.test.ts: -------------------------------------------------------------------------------- 1 | import { normalizeIndent } from "@engraft/shared/lib/normalizeIndent.js"; 2 | import { makeSlotWithCode } from "@engraft/tool-slot"; 3 | import { spawnSync } from "node:child_process"; 4 | import { writeFileSync } from "node:fs"; 5 | import { fileURLToPath } from 'node:url'; 6 | import * as tmp from "tmp"; 7 | import { describe, expect, it } from "vitest"; 8 | 9 | function relative(path: string) { 10 | return fileURLToPath(new URL(path, import.meta.url)); 11 | } 12 | 13 | tmp.setGracefulCleanup(); 14 | 15 | describe('run.js', () => { 16 | it('basically works', () => { 17 | const input = normalizeIndent` 18 | abc 19 | def 20 | ghi 21 | `; 22 | 23 | const program = JSON.stringify(makeSlotWithCode( 24 | 'IDinput000000.map((x) => x.toUpperCase())' 25 | )); 26 | 27 | const programFile = tmp.fileSync(); 28 | writeFileSync(programFile.name, program); 29 | const result = spawnSync( 30 | "node", 31 | [ 32 | relative('../lib/run.js'), 33 | programFile.name 34 | ], 35 | { 36 | encoding: 'utf8', 37 | input, 38 | } 39 | ); 40 | expect(result.status).toEqual(0); 41 | expect(result.stdout).toEqual(normalizeIndent` 42 | ABC 43 | DEF 44 | GHI 45 | `); 46 | }); 47 | }, { 48 | timeout: 2000 49 | }); 50 | -------------------------------------------------------------------------------- /packages/cli/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/cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ], 4 | "compilerOptions": { 5 | "types": [ 6 | "vite/client" 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/codemirror-helpers/README.md: -------------------------------------------------------------------------------- 1 | # CHANGEME 2 | 3 | All about the lib! 4 | -------------------------------------------------------------------------------- /packages/codemirror-helpers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/codemirror-helpers", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Some shared utilities to manage CodeMirror instances", 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 | "@codemirror/autocomplete": "^6.4.2", 21 | "@codemirror/commands": "^6.2.1", 22 | "@codemirror/language": "^6.6.0", 23 | "@codemirror/lint": "^6.2.0", 24 | "@codemirror/search": "^6.2.3", 25 | "@codemirror/state": "^6.1.1", 26 | "@codemirror/view": "^6.2.0", 27 | "@engraft/core-widgets": "^0.0.9", 28 | "@engraft/shared": "^0.0.9", 29 | "@engraft/toolkit": "^0.0.9", 30 | "lodash": "^4.17.21", 31 | "react": "^18.0.0", 32 | "react-dom": "^18.0.0" 33 | }, 34 | "devDependencies": { 35 | "@types/lodash": "^4.14.178" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/codemirror-helpers/src/PortalWidget.tsx: -------------------------------------------------------------------------------- 1 | import { WidgetType } from "@codemirror/view"; 2 | import { useMemo, useState } from "react"; 3 | 4 | 5 | // a CodeMirror widget which reports to a PortalSet for React rendering 6 | 7 | export class PortalWidget extends WidgetType { 8 | elem?: HTMLSpanElement; 9 | 10 | constructor(readonly portalSet: PortalSet, readonly data: T) { 11 | super() 12 | } 13 | 14 | eq(otherWidget: PortalWidget) { 15 | return this.portalSet === otherWidget.portalSet; 16 | } 17 | 18 | toDOM() { 19 | this.elem = document.createElement("span") 20 | this.portalSet.register(this.elem, this.data); 21 | return this.elem; 22 | } 23 | 24 | destroy() { 25 | this.elem && this.portalSet.unregister(this.elem); 26 | } 27 | } 28 | 29 | // a generic tool for maintaining sets of React portals coming out of an imperative system 30 | // (like CodeMirror!) 31 | 32 | export class PortalSet { 33 | private portalMap: Map = new Map(); 34 | 35 | constructor(readonly setPortals: (portals: [HTMLSpanElement, T][]) => void) {} 36 | 37 | private portals(): [HTMLSpanElement, T][] { 38 | return Array.from(this.portalMap.entries()); 39 | } 40 | 41 | register(elem: HTMLSpanElement, data: T) { 42 | this.portalMap.set(elem, data); 43 | this.setPortals(this.portals()) 44 | } 45 | 46 | unregister(elem: HTMLSpanElement) { 47 | this.portalMap.delete(elem); 48 | this.setPortals(this.portals()) 49 | } 50 | } 51 | 52 | export function usePortalSet() { 53 | const [portals, setPortals] = useState<[HTMLSpanElement, T][]>([]) 54 | const portalSet = useMemo(() => new PortalSet(setPortals), []); 55 | 56 | return [portalSet, portals] as const; 57 | } 58 | -------------------------------------------------------------------------------- /packages/codemirror-helpers/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./CodeMirror.js"; 2 | export * from "./codeMirrorStuff.js"; 3 | export * from "./FancyCodeEditor.js"; 4 | export * from "./refs.js"; 5 | -------------------------------------------------------------------------------- /packages/codemirror-helpers/src/refs.ts: -------------------------------------------------------------------------------- 1 | // TODO: extended this from a-z so I could shove js variable names in; not great 2 | export const idRegExp = "ID[a-zA-Z_]*[0-9]{6}"; 3 | 4 | export function refCode(s: string) { 5 | // currently, the id of a reference is just embedded directly into code 6 | return s; 7 | } 8 | 9 | export const refREAll = new RegExp(refCode(`(${idRegExp})`), "g") 10 | 11 | export const refREDirect = new RegExp(refCode(`(${idRegExp})(?!_)`), "g") 12 | export function referencesFromCodeDirect(code: string): Set { 13 | // TODO: this is not principled or robust; should probably actually parse the code? 14 | // (but then we'd want to share some work to avoid parsing twice? idk) 15 | return new Set([...code.matchAll(refREDirect)].map(m => m[1])); 16 | } 17 | 18 | export const refREPromise = new RegExp(refCode(`(${idRegExp})_promise`), "g") 19 | export function referencesFromCodePromise(code: string): Set { 20 | // TODO: this is not principled or robust; should probably actually parse the code? 21 | // (but then we'd want to share some work to avoid parsing twice? idk) 22 | return new Set([...code.matchAll(refREPromise)].map(m => m[1])); 23 | } 24 | -------------------------------------------------------------------------------- /packages/codemirror-helpers/src/useInterval.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export default function useInterval(callback: () => void, delay: number | null) { 4 | const savedCallback = React.useRef(callback) 5 | 6 | // Remember the latest callback if it changes. 7 | React.useLayoutEffect(() => { 8 | savedCallback.current = callback 9 | }, [callback]) 10 | 11 | // Set up the interval. 12 | React.useEffect(() => { 13 | // Don't schedule if no delay is specified. 14 | // Note: 0 is a valid value for delay. 15 | if (!delay && delay !== 0) { 16 | return 17 | } 18 | 19 | const id = setInterval(() => savedCallback.current(), delay) 20 | 21 | return () => clearInterval(id) 22 | }, [delay]) 23 | } 24 | -------------------------------------------------------------------------------- /packages/codemirror-helpers/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/codemirror-helpers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/core-widgets/README.md: -------------------------------------------------------------------------------- 1 | # @engraft/core-widgets 2 | 3 | This package defines some common interface bits used across Engraft tools, as React components. 4 | Long-term identity of this package is TBD. 5 | Perhaps some bits will be standardized and other bits put elsewhere? 6 | -------------------------------------------------------------------------------- /packages/core-widgets/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/core-widgets", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Some common React components.", 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/react": "^0.0.9", 22 | "@engraft/shared": "^0.0.9", 23 | "@engraft/update-proxy": "^0.0.9", 24 | "react-inspector": "^6.0.1", 25 | "react-merge-refs": "^2.0.1" 26 | }, 27 | "peerDependencies": { 28 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/core-widgets/src/IsolateStyles.tsx: -------------------------------------------------------------------------------- 1 | import rootCss from "./root.css.js"; 2 | import { HTMLProps, memo } from "react"; 3 | 4 | export const RootStyles = memo(function RootStyles() { 5 | return 8 | }); 9 | 10 | // TODO: this has a div in it, which forces block layout 11 | export const IsolateStyles = memo(function IsolateStyles({children, ...props}: HTMLProps) { 12 | return
13 | 14 |
15 | {children} 16 |
17 |
; 18 | }) 19 | -------------------------------------------------------------------------------- /packages/core-widgets/src/MyContextMenu.css: -------------------------------------------------------------------------------- 1 | .MyContextMenu { 2 | z-index: 100; 3 | background-color: #fffa; 4 | backdrop-filter: blur(10px); 5 | background-clip: padding-box; 6 | border: 1px solid rgba(0,0,0,.15); 7 | border-radius: 10px; 8 | color: #373a3c; 9 | font-size: 16px; 10 | margin: 2px 0 0; 11 | min-width: 160px; 12 | outline: none; 13 | padding: 10px; 14 | text-align: left; 15 | /* transition: opacity 250ms ease !important; */ 16 | } 17 | 18 | 19 | .MyContextMenu-divider { 20 | border-bottom: 1px solid rgba(0,0,0,.15); 21 | cursor: inherit; 22 | margin-bottom: 3px; 23 | padding: 2px 0; 24 | } 25 | 26 | .MyContextMenu-divider:first-child { 27 | margin-top: -3px; 28 | } 29 | -------------------------------------------------------------------------------- /packages/core-widgets/src/MyContextMenu.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from "react"; 2 | import myContextMenuCss from "./MyContextMenu.css.js"; 3 | 4 | export const MyContextMenu = memo(function MyContextMenu(props: { children: React.ReactNode }) { 5 | const { children } = props; 6 | 7 | return <> 8 | 9 |
10 | {children} 11 |
12 | ; 13 | }); 14 | 15 | export const MyContextMenuHeading = memo(function MyContextMenuHeading(props: { children: React.ReactNode }) { 16 | const { children } = props; 17 | return
18 | {children} 24 |
25 | }); 26 | -------------------------------------------------------------------------------- /packages/core-widgets/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./IsolateStyles.js"; 2 | export * from "./MyContextMenu.js"; 3 | export * from "./ScrollShadow.js"; 4 | export * from "./Value.js"; 5 | export * from "./ValueEditable.js"; 6 | export * from "./Vars.js"; 7 | -------------------------------------------------------------------------------- /packages/core-widgets/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-widgets/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/core", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Engraft core protocol", 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 | "@engraft/shared": "^0.0.9", 22 | "friendly-words": "^1.2.0", 23 | "synchronous-promise": "^2.0.16" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/EngraftPromise-refunc.ts: -------------------------------------------------------------------------------- 1 | import { hookLater } from "@engraft/refunc"; 2 | import { EngraftPromise } from "./EngraftPromise.js"; 3 | 4 | // Convenience function for running hooks asynchronously. 5 | // Memoizes nothing. 6 | export function hookThen(promise: EngraftPromise, 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 | {children}; 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": " IDsailor264826.default()}\n>\n Celebrate!\n", 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": "\n \n", 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 { 2 | onProgress?(progress: Progress): void; 3 | onComplete?(complete: Complete): void 4 | } 5 | 6 | export class Task { 7 | started = false; 8 | cancelled = false; 9 | 10 | constructor(readonly generator: Generator, readonly options: TaskOptions = {}) { 11 | 12 | } 13 | 14 | start() { 15 | this.requestStep(); 16 | } 17 | 18 | cancel() { 19 | this.cancelled = true; 20 | } 21 | 22 | private requestStep() { 23 | requestIdleCallback((deadline) => this.step(deadline)); 24 | } 25 | 26 | private step(deadline: IdleDeadline) { 27 | // We assume the task can only be cancelled outside of this function. 28 | if (this.cancelled) { return; } 29 | 30 | let curr: IteratorResult;; 31 | while (true) { 32 | curr = this.generator.next(); 33 | if (curr.done || deadline.timeRemaining() === 0) { 34 | break; 35 | } 36 | } 37 | 38 | if (curr.done) { 39 | this.options.onComplete && this.options.onComplete(curr.value); 40 | } else { 41 | this.options.onProgress && this.options.onProgress(curr.value); 42 | this.requestStep(); 43 | } 44 | } 45 | } 46 | 47 | export function runToCompletion(generator: Generator, logProgress: boolean = false): Complete { 48 | while (true) { 49 | const curr = generator.next(); 50 | if (curr.done) { 51 | return curr.value; 52 | } else if (logProgress) { 53 | console.log(curr.value); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/tool-synthesizer/test/synthesizer.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest"; 2 | import { compileExpression } from "@engraft/shared/lib/compile.js"; 3 | import { runToCompletion } from "../lib/Task.js"; 4 | import { synthesizeGen } from "../lib/synthesizer.js"; 5 | 6 | describe('synthesizeGen', () => { 7 | it('synthesizes "adding one"', () => { 8 | const prompt: [any, any][] = [ 9 | [10, 11], 10 | [100, 101], 11 | ]; 12 | 13 | const result = runToCompletion(synthesizeGen(prompt)); 14 | expect( 15 | compileExpression(result!)({input: 1000}) 16 | ).toEqual(1001); 17 | }); 18 | 19 | it('synthesizes "first letters', () => { 20 | const prompt: [any, any][] = [ 21 | ["George Washington Carver", ["G", "W", "C"]], 22 | ["David Attenborough", ["D", "A"]], 23 | ]; 24 | 25 | const result = runToCompletion(synthesizeGen(prompt)); 26 | expect( 27 | compileExpression(result!)({input: "Herbie Hancock"}) 28 | ).toEqual(["H", "H"]); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/tool-synthesizer/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-synthesizer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/tool-test-count-runs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/tool-test-count-runs", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Engraft testing tool that displays how many times it has been run", 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-test-count-runs/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { CollectReferences, MakeProgram, ShowView, ToolProgram, ToolRun, ToolView, defineTool, hookMemo, hookRef, hookRunTool, hooks, memoizeProps, renderWithReact, up } from "@engraft/toolkit"; 2 | 3 | type Program = { 4 | toolName: 'test-count-runs', 5 | subProgram: ToolProgram, 6 | } 7 | 8 | const makeProgram: MakeProgram = (context, defaultInputCode) => ({ 9 | toolName: 'test-count-runs', 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 numRuns = hookRef(() => 0); 23 | numRuns.current++; 24 | 25 | const view: ToolView = hookMemo(() => ({ 26 | render: renderWithReact(({updateProgram}) => 27 |
28 |
29 | runs 30 |
{numRuns.current}
31 |
32 | 33 |
34 | ), 35 | }), [numRuns, subResult.view]); 36 | 37 | return {outputP, view}; 38 | })); 39 | 40 | export default defineTool({ name: 'test-count-runs', makeProgram, collectReferences, run }) 41 | -------------------------------------------------------------------------------- /packages/tool-test-count-runs/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-test-count-runs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/tool-text/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/tool-text", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Engraft tool for plain text", 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/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-text/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-text/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/tool-toy-adder-simple/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/tool-toy-adder-simple", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Toy Engraft tool for adding two numbers, implemented using defineSimpleTool", 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-toy-adder-simple/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { defineSimpleTool } from "@engraft/toolkit"; 2 | 3 | export default defineSimpleTool({ 4 | name: 'toy-adder-simple', 5 | fields: { }, 6 | subTools: ['x', 'y'], 7 | compute: ({ subToolOutputs }) => { 8 | if (typeof subToolOutputs.x !== 'number') { throw new Error('x must be a number'); } 9 | if (typeof subToolOutputs.y !== 'number') { throw new Error('y must be a number'); } 10 | return subToolOutputs.x + subToolOutputs.y; 11 | }, 12 | render: ({ renderSlot, autoFocus }) => ( 13 |
14 |
15 | x 16 | {renderSlot('x', {autoFocus})} 17 |
18 |
19 | y 20 | {renderSlot('y')} 21 |
22 |
23 | ), 24 | }) 25 | -------------------------------------------------------------------------------- /packages/tool-toy-adder-simple/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-toy-adder-simple/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/tool-toy-adder-vanilla/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/tool-toy-adder-vanilla", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Toy Engraft tool for adding two numbers (written without 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/toolkit": "^0.0.9" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/tool-toy-adder-vanilla/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-toy-adder-vanilla/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/tool-toy-adder/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/tool-toy-adder", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Toy Engraft tool for adding two numbers", 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-toy-adder/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-toy-adder/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/tool-toy-hello-world-vanilla/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/tool-toy-hello-world-vanilla", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Toy Engraft tool that says hello (written without 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 | }, 16 | "main": "lib/index.js", 17 | "type": "module", 18 | "dependencies": { 19 | "@engraft/toolkit": "^0.0.9" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/tool-toy-hello-world-vanilla/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { EngraftPromise, ToolView, defineTool, hookMemo, hooks, memoizeProps } from "@engraft/toolkit"; 2 | 3 | type Program = { 4 | toolName: 'toy-hello-world-vanilla', 5 | } 6 | 7 | export default defineTool({ 8 | name: 'toy-hello-world-vanilla', 9 | 10 | makeProgram: () => ({ 11 | toolName: 'toy-hello-world-vanilla', 12 | }), 13 | 14 | collectReferences: (_program) => [], 15 | 16 | run: memoizeProps(hooks((_props) => { 17 | const outputP = hookMemo(() => EngraftPromise.resolve({ 18 | value: "Output: Hello world!" 19 | }), []); 20 | 21 | const view: ToolView = hookMemo(() => ({ 22 | render: (_memory, _props, element) => { 23 | // do the simplest possible thing (will re-run a lot) 24 | element.innerHTML = '

View: Hello world!

'; 25 | }, 26 | }), []); 27 | 28 | return { outputP, view }; 29 | })), 30 | }); 31 | -------------------------------------------------------------------------------- /packages/tool-toy-hello-world-vanilla/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-toy-hello-world-vanilla/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/tool-toy-hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/tool-toy-hello-world", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Toy Engraft tool that says hello", 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-toy-hello-world/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { EngraftPromise, ToolView, defineTool, hookMemo, hooks, memoizeProps, renderWithReact } from "@engraft/toolkit"; 2 | 3 | type Program = { 4 | toolName: 'toy-hello-world', 5 | } 6 | 7 | export default defineTool({ 8 | name: 'toy-hello-world', 9 | 10 | makeProgram: () => ({ 11 | toolName: 'toy-hello-world', 12 | }), 13 | 14 | collectReferences: (_program) => [], 15 | 16 | run: memoizeProps(hooks((_props) => { 17 | const outputP = hookMemo(() => EngraftPromise.resolve({ 18 | value: "Output: Hello world!" 19 | }), []); 20 | 21 | const view: ToolView = hookMemo(() => ({ 22 | render: renderWithReact(() =>

View: Hello world!

) 23 | }), []); 24 | 25 | return { outputP, view }; 26 | })), 27 | }); 28 | -------------------------------------------------------------------------------- /packages/tool-toy-hello-world/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-toy-hello-world/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/tool-value/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/tool-value", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Engraft tool which covers a slot with its value", 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 | "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/tool-value/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-value/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/tool-vite-lib/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/tool-vite-lib", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Engraft tool which provides access to live-reloading libraries compiled by Vite", 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 | "humanize-duration": "^3.31.0" 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 | "devDependencies": { 28 | "@types/humanize-duration": "^3.27.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/tool-vite-lib/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-vite-lib/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/tool-voyager/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/tool-voyager", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Engraft tool for Voyager 2", 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 | "@engraft/vendor-voyager": "^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-voyager/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-voyager/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/toolkit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/toolkit", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "A bundle of libraries needful or helpful for defining Engraft tools", 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 | "lodash": "^4.17.21" 27 | }, 28 | "peerDependencies": { 29 | "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/toolkit/src/index.ts: -------------------------------------------------------------------------------- 1 | import { CSSProperties } from "react"; 2 | 3 | export * from "@engraft/core"; 4 | export * from "@engraft/react"; 5 | export * from "@engraft/core-widgets"; 6 | export * from "@engraft/refunc-react"; 7 | export * from "@engraft/update-proxy"; 8 | export * from "./simple-tool.js"; 9 | export * from "./CommonWidth.js"; 10 | export * from "./input.js"; 11 | export * from "./cellNetwork.js"; 12 | 13 | export const outputBackgroundColor = "hsl(120, 50%, 97%)"; 14 | 15 | export const outputBackgroundStyle: CSSProperties = { 16 | backgroundColor: outputBackgroundColor, 17 | ...{ "--shadow-color": outputBackgroundColor }, 18 | } 19 | -------------------------------------------------------------------------------- /packages/toolkit/src/input.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | import { CommonWidth } from './CommonWidth.js'; 3 | 4 | // There's an emerging convention of putting an "input" field at the top of a 5 | // tool. This is a set of components to do that in a somewhat uniform way. 6 | 7 | export const inputBackground = 'rgba(251, 251, 255)'; 8 | 9 | export const inputFrameBarBackdrop =
; 10 | 11 | export const InputHeading = memo(function InputHeading(props: { 12 | slot: React.ReactElement, 13 | headingCommonWidth?: CommonWidth, 14 | }) { 15 | const { slot, headingCommonWidth } = props; 16 | 17 | const icon =
{inputIcon}
; 18 | 19 | return ( 20 |
27 | { headingCommonWidth 28 | ? headingCommonWidth.wrap(icon, 'right') 29 | : icon 30 | } 31 | {slot} 32 |
33 | ); 34 | }); 35 | 36 | export const inputIcon = 37 | 38 | 39 | 40 | 41 | 42 | 43 | ; 44 | -------------------------------------------------------------------------------- /packages/toolkit/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/toolkit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/update-proxy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/update-proxy", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "A little gadget to make it easier to perform functional updates", 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 | "immutability-helper": "^3.1.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/update-proxy/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/update-proxy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/use-engraft-demo-js/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Synonymizer (via useEngraft, TypeScript) 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/use-engraft-demo-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@engraft/use-engraft-demo-js", 4 | "version": "0.0.9", 5 | "license": "MIT", 6 | "description": "Synonymizer: demo of useEngraft built with JavaScript", 7 | "type": "module", 8 | "scripts": { 9 | "depcheck": "node ../../scripts/our-depcheck.cjs", 10 | "build-app": "vite build . --config ../../vite.config.ts", 11 | "preview": "vite preview . --config ../../vite.config.ts", 12 | "dev": "vite . --config ../../vite.config.ts" 13 | }, 14 | "dependencies": { 15 | "@engraft/use-engraft": "^0.0.9", 16 | "react": "^18.0.0", 17 | "react-dom": "^18.0.0", 18 | "react-router-dom": "^6.3.0", 19 | "vite": "^4.1.4" 20 | }, 21 | "devDependencies": { 22 | "@types/react-dom": "^18.0.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/use-engraft-demo-js/src/App.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | position: relative; 3 | width: 100%; 4 | height: 100%; 5 | } 6 | 7 | body, :host { 8 | color: #333; 9 | margin: 0; 10 | padding: 8px; 11 | box-sizing: border-box; 12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 13 | } 14 | 15 | a { 16 | color: rgb(0,100,200); 17 | text-decoration: none; 18 | } 19 | 20 | a:hover { 21 | text-decoration: underline; 22 | } 23 | 24 | a:visited { 25 | color: rgb(0,80,160); 26 | } 27 | 28 | label { 29 | display: block; 30 | } 31 | 32 | input, button, select, textarea { 33 | font-family: inherit; 34 | font-size: inherit; 35 | -webkit-padding: 0.4em 0; 36 | padding: 0.4em; 37 | margin: 0 0 0.5em 0; 38 | box-sizing: border-box; 39 | border: 1px solid #ccc; 40 | border-radius: 2px; 41 | } 42 | 43 | input:disabled { 44 | color: #ccc; 45 | } 46 | 47 | button { 48 | color: #333; 49 | background-color: #f4f4f4; 50 | outline: none; 51 | } 52 | 53 | button:disabled { 54 | color: #999; 55 | } 56 | 57 | button:not(:disabled):active { 58 | background-color: #ddd; 59 | } 60 | 61 | button:focus { 62 | border-color: #666; 63 | } 64 | 65 | 66 | 67 | 68 | main { 69 | padding: 1em; 70 | max-width: 240px; 71 | margin: 0 auto; 72 | text-align: center; 73 | max-width: none; 74 | } 75 | 76 | header { 77 | margin-bottom: 20px; 78 | } 79 | 80 | h1 { 81 | color: #ff3e00; 82 | text-transform: uppercase; 83 | font-size: 4em; 84 | font-weight: 100; 85 | margin-top: 0; 86 | } 87 | 88 | .word { 89 | display: inline-block; 90 | padding: 20px; 91 | cursor: pointer; 92 | } 93 | -------------------------------------------------------------------------------- /packages/use-engraft-demo-js/src/extractSynonymsProgram.json: -------------------------------------------------------------------------------- 1 | null 2 | -------------------------------------------------------------------------------- /packages/use-engraft-demo-js/src/index.jsx: -------------------------------------------------------------------------------- 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.jsx"; 5 | 6 | const root = createRoot(document.getElementById('root')); 7 | 8 | root.render( 9 | 10 | 11 | 12 | }/> 13 | 14 | 15 | , 16 | ); 17 | -------------------------------------------------------------------------------- /packages/use-engraft-demo-js/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/use-engraft-demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Synonymizer (via useEngraft, TypeScript) 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/use-engraft-demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "@engraft/use-engraft-demo", 4 | "version": "0.0.9", 5 | "license": "MIT", 6 | "description": "Synonymizer: demo of useEngraft built with TypeScript", 7 | "type": "module", 8 | "scripts": { 9 | "depcheck": "node ../../scripts/our-depcheck.cjs", 10 | "lint": "eslint --max-warnings=0 .", 11 | "tsc": "tsc", 12 | "build-app": "vite build --config ../../vite.config.ts", 13 | "preview": "vite preview --config ../../vite.config.ts", 14 | "dev": "vite --config ../../vite.config.ts" 15 | }, 16 | "dependencies": { 17 | "@engraft/use-engraft": "^0.0.9", 18 | "react": "^18.0.0", 19 | "react-dom": "^18.0.0", 20 | "react-router-dom": "^6.3.0" 21 | }, 22 | "devDependencies": { 23 | "@types/react-dom": "^18.0.0", 24 | "vite": "^4.3.2" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/use-engraft-demo/src/App.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | position: relative; 3 | width: 100%; 4 | height: 100%; 5 | } 6 | 7 | body, :host { 8 | color: #333; 9 | margin: 0; 10 | padding: 8px; 11 | box-sizing: border-box; 12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 13 | } 14 | 15 | a { 16 | color: rgb(0,100,200); 17 | text-decoration: none; 18 | } 19 | 20 | a:hover { 21 | text-decoration: underline; 22 | } 23 | 24 | a:visited { 25 | color: rgb(0,80,160); 26 | } 27 | 28 | label { 29 | display: block; 30 | } 31 | 32 | input, button, select, textarea { 33 | font-family: inherit; 34 | font-size: inherit; 35 | -webkit-padding: 0.4em 0; 36 | padding: 0.4em; 37 | margin: 0 0 0.5em 0; 38 | box-sizing: border-box; 39 | border: 1px solid #ccc; 40 | border-radius: 2px; 41 | } 42 | 43 | input:disabled { 44 | color: #ccc; 45 | } 46 | 47 | button { 48 | color: #333; 49 | background-color: #f4f4f4; 50 | outline: none; 51 | } 52 | 53 | button:disabled { 54 | color: #999; 55 | } 56 | 57 | button:not(:disabled):active { 58 | background-color: #ddd; 59 | } 60 | 61 | button:focus { 62 | border-color: #666; 63 | } 64 | 65 | 66 | 67 | 68 | main { 69 | padding: 1em; 70 | max-width: 240px; 71 | margin: 0 auto; 72 | text-align: center; 73 | max-width: none; 74 | } 75 | 76 | header { 77 | margin-bottom: 20px; 78 | } 79 | 80 | h1 { 81 | color: #ff3e00; 82 | text-transform: uppercase; 83 | font-size: 4em; 84 | font-weight: 100; 85 | margin-top: 0; 86 | } 87 | 88 | .word { 89 | display: inline-block; 90 | padding: 20px; 91 | cursor: pointer; 92 | } 93 | -------------------------------------------------------------------------------- /packages/use-engraft-demo/src/extractSynonymsProgram.json: -------------------------------------------------------------------------------- 1 | null 2 | -------------------------------------------------------------------------------- /packages/use-engraft-demo/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 | -------------------------------------------------------------------------------- /packages/use-engraft-demo/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/use-engraft/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/use-engraft", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "A React hook for embedding Engraft tools", 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/fancy-setup": "^0.0.9", 21 | "@engraft/hostkit": "^0.0.9", 22 | "@engraft/shared": "^0.0.9", 23 | "idb-keyval": "^6.2.0", 24 | "lodash": "^4.17.21" 25 | }, 26 | "devDependencies": { 27 | "@types/wicg-file-system-access": "^2020.9.5" 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/use-engraft/src/index.css: -------------------------------------------------------------------------------- 1 | .UseEngraftEditor { 2 | font-family: sans-serif; 3 | 4 | /* margin: 16px; */ 5 | height: 100vh; 6 | display: flex; 7 | flex-direction: column; 8 | /* background: #F0F2F5; */ 9 | } 10 | 11 | .UseEngraftEditor .card { 12 | margin-bottom: 16px; 13 | background: white; 14 | border-radius: 6px; 15 | padding: 12px; 16 | box-shadow: 0 1px 4px rgba(20,20,20,0.2); 17 | display: flex; 18 | flex-direction: column; 19 | min-height: 0; 20 | } 21 | 22 | .UseEngraftEditor pre { 23 | margin-top: 0px; 24 | margin-bottom: 0px; 25 | } 26 | 27 | .UseEngraftEditor > .heading > h1 { 28 | all: unset; 29 | font-size: 2em; 30 | font-family: monospace; 31 | font-style: italic; 32 | } 33 | 34 | .UseEngraftEditor > .heading > button { 35 | background-color: rgb(239, 239, 239); 36 | } 37 | 38 | .UseEngraftEditor > .content > .card > h2 { 39 | margin: 0; 40 | margin-bottom: 10px; 41 | /* text-align: right; */ 42 | /* font-weight: normal; 43 | font-style: italic; */ 44 | } 45 | 46 | .UseEngraftEditor > .heading { 47 | padding: 16px; 48 | } 49 | 50 | .UseEngraftEditor > .content { 51 | padding: 16px; 52 | overflow: auto; 53 | padding-top: 0; 54 | } 55 | -------------------------------------------------------------------------------- /packages/use-engraft/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/use-engraft/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": [ "src", "test", "../../types/lib.d.ts" ] 4 | } 5 | -------------------------------------------------------------------------------- /packages/vendor-voyager/README.md: -------------------------------------------------------------------------------- 1 | # @engraft/vendor-voyager 2 | 3 | This package vendors in a compiled, slightly patched version of [Voyager](https://github.com/vega/voyager). It is used in @engraft/tool-voyager. 4 | 5 | (Why? The bundled version of Voyager available on npm does not expose the bits we want. We can't access unbundled bits because that exposes unbuilt pieces like Sass, and that's a mess. Hence, we need to patch. Compiling a patched version requires an older version of Node (16), so it's easier to just build it separately and then copy it in here.) 6 | 7 | Note: For this package to compile with Vite, we need to add it to the `optimizeDeps` list in the monorepo's root `vite.config.ts`: 8 | ``` 9 | optimizeDeps: { 10 | include: [ 11 | "@engraft/vendor-voyager", 12 | ] 13 | } 14 | ``` 15 | 16 | Also note that Voyager's styles have been moved to `@engraft/tool-voyager` because it was too much trouble to keep them here (ES vs CJS issues). 17 | -------------------------------------------------------------------------------- /packages/vendor-voyager/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@engraft/vendor-voyager", 3 | "version": "0.0.9", 4 | "license": "MIT", 5 | "description": "Vendored-in Voyager for @engraft/tool-voyager", 6 | "publishConfig": { 7 | "access": "public" 8 | }, 9 | "scripts": {}, 10 | "main": "src/lib-voyager.cjs" 11 | } 12 | -------------------------------------------------------------------------------- /scripts/our-depcheck.cjs: -------------------------------------------------------------------------------- 1 | const depcheck = require('depcheck'); 2 | 3 | const options = { 4 | // tests will use devDependencies from the root package.json; ignore them 5 | ignorePatterns: ["/test/**", "/dist/**", "/lib/**", "vite.config.ts"], 6 | // dynamic imports from the web should be ignored 7 | ignoreMatches: ["https:*"] 8 | }; 9 | 10 | depcheck(process.cwd(), options).then((unused) => { 11 | let problem = false; 12 | 13 | if (unused.dependencies.length > 0) { 14 | console.log("Unused dependencies"); 15 | for (const dep of unused.dependencies) { 16 | console.log(` ${dep}`); 17 | } 18 | problem = true; 19 | } 20 | 21 | const missing = Object.keys(unused.missing); 22 | if (missing.length > 0) { 23 | console.log("Missing dependencies"); 24 | for (const dep of missing) { 25 | console.log(` ${dep}`); 26 | } 27 | problem = true; 28 | } 29 | 30 | const invalidFiles = Object.keys(unused.invalidFiles); 31 | if (invalidFiles.length > 0) { 32 | console.log("Invalid files"); 33 | for (const file of invalidFiles) { 34 | console.log(` ${file}: ${unused.invalidFiles[file]}`); 35 | } 36 | problem = true; 37 | } 38 | 39 | // TODO: unused.invalidDirs? 40 | 41 | if (problem) { 42 | process.exitCode = 1; 43 | } else { 44 | console.log("No problems found by our-depcheck.cjs"); 45 | } 46 | }); 47 | -------------------------------------------------------------------------------- /templates/app/README.md: -------------------------------------------------------------------------------- 1 | # CHANGEME 2 | 3 | All about the app! 4 | -------------------------------------------------------------------------------- /templates/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | CHANGEME: title 7 | 8 | 9 | 10 |
11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /templates/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "CHANGEME: package name", 4 | "version": "0.0.1", 5 | "license": "MIT", 6 | "description": "CHANGEME: package description", 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 | "dependencies": { 16 | "react": "^18.0.0", 17 | "react-router-dom": "^6.3.0", 18 | "react-dom": "^18.0.0" 19 | }, 20 | "devDependencies": { 21 | "@types/react-dom": "^18.0.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /templates/app/src/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 100px; 3 | } 4 | -------------------------------------------------------------------------------- /templates/app/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from "react"; 2 | import css from "./App.css?inline"; 3 | 4 | export const App = memo(function App(props: {}) { 5 | return
6 | 7 |

CHANGEME: contents

8 |
; 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 --------------------------------------------------------------------------------