@dxos/plugin-script 0.6.10 → 0.6.11-staging.9c4dc38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/lib/browser/{ScriptEditor-WNCGR5OU.mjs → ScriptEditor-5Y3EROG3.mjs} +126 -65
  2. package/dist/lib/browser/ScriptEditor-5Y3EROG3.mjs.map +7 -0
  3. package/dist/lib/browser/{TypescriptEditor-XTGRVCMY.mjs → TypescriptEditor-GPE72Y6I.mjs} +2 -2
  4. package/dist/lib/browser/{chunk-DX5CSOBP.mjs → chunk-MRGOT5RF.mjs} +25 -26
  5. package/dist/lib/browser/chunk-MRGOT5RF.mjs.map +7 -0
  6. package/dist/lib/browser/{chunk-ZGCETT2W.mjs → chunk-O4LWBX6A.mjs} +7 -2
  7. package/dist/lib/browser/chunk-O4LWBX6A.mjs.map +7 -0
  8. package/dist/lib/browser/index.mjs +6 -6
  9. package/dist/lib/browser/index.mjs.map +2 -2
  10. package/dist/lib/browser/meta.json +1 -1
  11. package/dist/lib/browser/types/index.mjs +1 -1
  12. package/dist/lib/node/{ScriptEditor-RR7AVOQL.cjs → ScriptEditor-YCUQFWQM.cjs} +142 -81
  13. package/dist/lib/node/ScriptEditor-YCUQFWQM.cjs.map +7 -0
  14. package/dist/lib/node/{TypescriptEditor-CRONCQSI.cjs → TypescriptEditor-M3WWFIP2.cjs} +7 -7
  15. package/dist/lib/node/{TypescriptEditor-CRONCQSI.cjs.map → TypescriptEditor-M3WWFIP2.cjs.map} +2 -2
  16. package/dist/lib/node/{chunk-VW2POILW.cjs → chunk-5RBVXKXT.cjs} +9 -4
  17. package/dist/lib/node/chunk-5RBVXKXT.cjs.map +7 -0
  18. package/dist/lib/node/{chunk-RXI576K3.cjs → chunk-IMLDKD5Y.cjs} +28 -29
  19. package/dist/lib/node/chunk-IMLDKD5Y.cjs.map +7 -0
  20. package/dist/lib/node/index.cjs +18 -18
  21. package/dist/lib/node/index.cjs.map +2 -2
  22. package/dist/lib/node/meta.json +1 -1
  23. package/dist/lib/node/types/index.cjs +4 -4
  24. package/dist/lib/node/types/index.cjs.map +1 -1
  25. package/dist/types/src/ScriptPlugin.d.ts.map +1 -1
  26. package/dist/types/src/components/DebugPanel.d.ts +9 -0
  27. package/dist/types/src/components/DebugPanel.d.ts.map +1 -0
  28. package/dist/types/src/components/ScriptBlock/ScriptBlock.stories.d.ts +1 -4
  29. package/dist/types/src/components/ScriptBlock/ScriptBlock.stories.d.ts.map +1 -1
  30. package/dist/types/src/components/ScriptEditor.d.ts.map +1 -1
  31. package/dist/types/src/components/Toolbar.d.ts.map +1 -1
  32. package/dist/types/src/components/Toolbar.stories.d.ts +1 -3
  33. package/dist/types/src/components/Toolbar.stories.d.ts.map +1 -1
  34. package/dist/types/src/components/TypescriptEditor.d.ts +3 -4
  35. package/dist/types/src/components/TypescriptEditor.d.ts.map +1 -1
  36. package/dist/types/src/components/TypescriptEditor.stories.d.ts +1 -4
  37. package/dist/types/src/components/TypescriptEditor.stories.d.ts.map +1 -1
  38. package/dist/types/src/components/index.d.ts +1 -1
  39. package/dist/types/src/types/script.d.ts +12 -0
  40. package/dist/types/src/types/script.d.ts.map +1 -1
  41. package/dist/types/src/types/types.d.ts.map +1 -1
  42. package/package.json +36 -33
  43. package/src/ScriptPlugin.tsx +7 -4
  44. package/src/components/{DetailsPanel.tsx → DebugPanel.tsx} +87 -36
  45. package/src/components/ScriptBlock/ScriptBlock.stories.tsx +27 -20
  46. package/src/components/ScriptEditor.tsx +20 -5
  47. package/src/components/Toolbar.stories.tsx +2 -4
  48. package/src/components/Toolbar.tsx +15 -29
  49. package/src/components/TypescriptEditor.stories.tsx +9 -12
  50. package/src/components/TypescriptEditor.tsx +15 -17
  51. package/src/types/script.ts +13 -0
  52. package/dist/lib/browser/ScriptEditor-WNCGR5OU.mjs.map +0 -7
  53. package/dist/lib/browser/chunk-DX5CSOBP.mjs.map +0 -7
  54. package/dist/lib/browser/chunk-ZGCETT2W.mjs.map +0 -7
  55. package/dist/lib/node/ScriptEditor-RR7AVOQL.cjs.map +0 -7
  56. package/dist/lib/node/chunk-RXI576K3.cjs.map +0 -7
  57. package/dist/lib/node/chunk-VW2POILW.cjs.map +0 -7
  58. package/dist/types/src/components/DetailsPanel.d.ts +0 -9
  59. package/dist/types/src/components/DetailsPanel.d.ts.map +0 -1
  60. package/dist/types/src/data/templates.md.d.ts +0 -3
  61. package/dist/types/src/data/templates.md.d.ts.map +0 -1
  62. package/src/data/templates.md.ts +0 -44
  63. /package/dist/lib/browser/{TypescriptEditor-XTGRVCMY.mjs.map → TypescriptEditor-GPE72Y6I.mjs.map} +0 -0
  64. /package/src/types/{types.tsx → types.ts} +0 -0
@@ -2,11 +2,8 @@ import '@dxos-theme';
2
2
  import React from 'react';
3
3
  declare const _default: {
4
4
  title: string;
5
- component: ({ id, extensions, env, initialValue, scrollTo, selection, className, scrollPastEnd, }: import("./TypescriptEditor").TypescriptEditorProps) => React.JSX.Element;
5
+ component: ({ id, scrollPastEnd, env, className, initialValue, extensions, scrollTo, selection, }: import("./TypescriptEditor").TypescriptEditorProps) => React.JSX.Element;
6
6
  render: () => React.JSX.Element;
7
- parameters: {
8
- layout: string;
9
- };
10
7
  };
11
8
  export default _default;
12
9
  export declare const Default: {};
@@ -1 +1 @@
1
- {"version":3,"file":"TypescriptEditor.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/TypescriptEditor.stories.tsx"],"names":[],"mappings":"AAeA,OAAO,aAAa,CAAC;AAErB,OAAO,KAAkB,MAAM,OAAO,CAAC;;;;;;;;;AAoCvC,wBAOE;AAEF,eAAO,MAAM,OAAO,IAAK,CAAC"}
1
+ {"version":3,"file":"TypescriptEditor.stories.d.ts","sourceRoot":"","sources":["../../../../src/components/TypescriptEditor.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,aAAa,CAAC;AAErB,OAAO,KAAkB,MAAM,OAAO,CAAC;;;;;;AA+CvC,wBAIE;AAEF,eAAO,MAAM,OAAO,IAAK,CAAC"}
@@ -1,4 +1,4 @@
1
1
  import React from 'react';
2
2
  export declare const ScriptEditor: React.LazyExoticComponent<({ env, script, role }: import("./ScriptEditor").ScriptEditorProps) => React.JSX.Element>;
3
- export declare const TypescriptEditor: React.LazyExoticComponent<({ id, extensions, env, initialValue, scrollTo, selection, className, scrollPastEnd, }: import("./TypescriptEditor").TypescriptEditorProps) => React.JSX.Element>;
3
+ export declare const TypescriptEditor: React.LazyExoticComponent<({ id, scrollPastEnd, env, className, initialValue, extensions, scrollTo, selection, }: import("./TypescriptEditor").TypescriptEditorProps) => React.JSX.Element>;
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -6,11 +6,13 @@ declare const ScriptType_base: import("@dxos/echo-schema").AbstractTypedObject<{
6
6
  } & {
7
7
  id: string;
8
8
  }>;
9
+ description?: string | undefined;
9
10
  changed?: boolean | undefined;
10
11
  } & {
11
12
  id: string;
12
13
  }, S.Struct.Encoded<{
13
14
  name: S.optional<typeof S.String>;
15
+ description: S.optional<typeof S.String>;
14
16
  source: ref<{
15
17
  content: string;
16
18
  } & {
@@ -18,9 +20,13 @@ declare const ScriptType_base: import("@dxos/echo-schema").AbstractTypedObject<{
18
20
  }>;
19
21
  changed: S.optional<typeof S.Boolean>;
20
22
  }>>;
23
+ /**
24
+ * Source script.
25
+ */
21
26
  export declare class ScriptType extends ScriptType_base {
22
27
  }
23
28
  declare const FunctionType_base: import("@dxos/echo-schema").AbstractTypedObject<{
29
+ name?: string | undefined;
24
30
  version: number;
25
31
  source?: import("@dxos/echo-schema").Ref<{
26
32
  name?: string | undefined;
@@ -29,6 +35,7 @@ declare const FunctionType_base: import("@dxos/echo-schema").AbstractTypedObject
29
35
  } & {
30
36
  id: string;
31
37
  }>;
38
+ description?: string | undefined;
32
39
  changed?: boolean | undefined;
33
40
  } & {
34
41
  id: string;
@@ -37,6 +44,7 @@ declare const FunctionType_base: import("@dxos/echo-schema").AbstractTypedObject
37
44
  } & {
38
45
  id: string;
39
46
  }, S.Struct.Encoded<{
47
+ name: S.optional<typeof S.String>;
40
48
  version: typeof S.Number;
41
49
  binding: S.optional<typeof S.String>;
42
50
  source: S.optional<ref<{
@@ -46,11 +54,15 @@ declare const FunctionType_base: import("@dxos/echo-schema").AbstractTypedObject
46
54
  } & {
47
55
  id: string;
48
56
  }>;
57
+ description?: string | undefined;
49
58
  changed?: boolean | undefined;
50
59
  } & {
51
60
  id: string;
52
61
  }>>;
53
62
  }>>;
63
+ /**
64
+ * Function deployment.
65
+ */
54
66
  export declare class FunctionType extends FunctionType_base {
55
67
  }
56
68
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"script.d.ts","sourceRoot":"","sources":["../../../../src/types/script.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,GAAG,EAAE,CAAC,EAAe,MAAM,mBAAmB,CAAC;;;;;;;;;;;;;;;;;;;;AAGxD,qBAAa,UAAW,SAAQ,eAK9B;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEL,qBAAa,YAAa,SAAQ,iBAMhC;CAAG"}
1
+ {"version":3,"file":"script.d.ts","sourceRoot":"","sources":["../../../../src/types/script.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,GAAG,EAAE,CAAC,EAAe,MAAM,mBAAmB,CAAC;;;;;;;;;;;;;;;;;;;;;;AAKxD;;GAEG;AACH,qBAAa,UAAW,SAAQ,eAQ9B;CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEL;;GAEG;AACH,qBAAa,YAAa,SAAQ,iBAQhC;CAAG"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/types/types.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,eAAe,EACf,oBAAoB,EACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIxD,oBAAY,YAAY;IACtB,MAAM,kCAA4B;CACnC;AAED,MAAM,MAAM,oBAAoB,GAAG,eAAe,GAChD,sBAAsB,GACtB,oBAAoB,GACpB,uBAAuB,GACvB,oBAAoB,GACpB,cAAc,GACd,aAAa,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/types/types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,uBAAuB,EACvB,eAAe,EACf,oBAAoB,EACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAIxD,oBAAY,YAAY;IACtB,MAAM,kCAA4B;CACnC;AAED,MAAM,MAAM,oBAAoB,GAAG,eAAe,GAChD,sBAAsB,GACtB,oBAAoB,GACpB,uBAAuB,GACvB,oBAAoB,GACpB,cAAc,GACd,aAAa,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-script",
3
- "version": "0.6.10",
3
+ "version": "0.6.11-staging.9c4dc38",
4
4
  "description": "Braneframe notebook plugin",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -71,30 +71,33 @@
71
71
  "iso-did": "^1.6.0",
72
72
  "lodash.get": "^4.4.2",
73
73
  "prettier": "^3.2.4",
74
+ "react-syntax-highlighter": "^15.5.0",
74
75
  "typescript": "^5.5.4",
75
- "@dxos/app-framework": "0.6.10",
76
- "@dxos/async": "0.6.10",
77
- "@dxos/crypto": "0.6.10",
78
- "@dxos/client-protocol": "0.6.10",
79
- "@dxos/debug": "0.6.10",
80
- "@dxos/client": "0.6.10",
81
- "@dxos/echo-schema": "0.6.10",
82
- "@dxos/keys": "0.6.10",
83
- "@dxos/invariant": "0.6.10",
84
- "@dxos/plugin-client": "0.6.10",
85
- "@dxos/plugin-explorer": "0.6.10",
86
- "@dxos/plugin-graph": "0.6.10",
87
- "@dxos/log": "0.6.10",
88
- "@dxos/plugin-markdown": "0.6.10",
89
- "@dxos/plugin-space": "0.6.10",
90
- "@dxos/plugin-stack": "0.6.10",
91
- "@dxos/plugin-theme": "0.6.10",
92
- "@dxos/protocols": "0.6.10",
93
- "@dxos/react-client": "0.6.10",
94
- "@dxos/react-ui-editor": "0.6.10",
95
- "@dxos/rpc": "0.6.10",
96
- "@dxos/rpc-tunnel": "0.6.10",
97
- "@dxos/util": "0.6.10"
76
+ "@dxos/app-framework": "0.6.11-staging.9c4dc38",
77
+ "@dxos/async": "0.6.11-staging.9c4dc38",
78
+ "@dxos/client-protocol": "0.6.11-staging.9c4dc38",
79
+ "@dxos/crypto": "0.6.11-staging.9c4dc38",
80
+ "@dxos/client": "0.6.11-staging.9c4dc38",
81
+ "@dxos/debug": "0.6.11-staging.9c4dc38",
82
+ "@dxos/invariant": "0.6.11-staging.9c4dc38",
83
+ "@dxos/echo-schema": "0.6.11-staging.9c4dc38",
84
+ "@dxos/keys": "0.6.11-staging.9c4dc38",
85
+ "@dxos/plugin-client": "0.6.11-staging.9c4dc38",
86
+ "@dxos/plugin-explorer": "0.6.11-staging.9c4dc38",
87
+ "@dxos/plugin-graph": "0.6.11-staging.9c4dc38",
88
+ "@dxos/plugin-markdown": "0.6.11-staging.9c4dc38",
89
+ "@dxos/log": "0.6.11-staging.9c4dc38",
90
+ "@dxos/plugin-space": "0.6.11-staging.9c4dc38",
91
+ "@dxos/plugin-stack": "0.6.11-staging.9c4dc38",
92
+ "@dxos/plugin-theme": "0.6.11-staging.9c4dc38",
93
+ "@dxos/protocols": "0.6.11-staging.9c4dc38",
94
+ "@dxos/react-client": "0.6.11-staging.9c4dc38",
95
+ "@dxos/react-ui-attention": "0.6.11-staging.9c4dc38",
96
+ "@dxos/react-ui-syntax-highlighter": "0.6.11-staging.9c4dc38",
97
+ "@dxos/rpc": "0.6.11-staging.9c4dc38",
98
+ "@dxos/react-ui-editor": "0.6.11-staging.9c4dc38",
99
+ "@dxos/rpc-tunnel": "0.6.11-staging.9c4dc38",
100
+ "@dxos/util": "0.6.11-staging.9c4dc38"
98
101
  },
99
102
  "devDependencies": {
100
103
  "@phosphor-icons/react": "^2.1.5",
@@ -104,20 +107,20 @@
104
107
  "react": "~18.2.0",
105
108
  "react-dom": "~18.2.0",
106
109
  "vite": "^5.3.4",
107
- "@dxos/random": "0.6.10",
108
- "@dxos/echo-generator": "0.6.10",
109
- "@dxos/react-ui": "0.6.10",
110
- "@dxos/storybook-utils": "0.6.10",
111
- "@dxos/react-ui-mosaic": "0.6.10",
112
- "@dxos/react-ui-theme": "0.6.10"
110
+ "@dxos/random": "0.6.11-staging.9c4dc38",
111
+ "@dxos/react-ui": "0.6.11-staging.9c4dc38",
112
+ "@dxos/react-ui-mosaic": "0.6.11-staging.9c4dc38",
113
+ "@dxos/echo-generator": "0.6.11-staging.9c4dc38",
114
+ "@dxos/react-ui-theme": "0.6.11-staging.9c4dc38",
115
+ "@dxos/storybook-utils": "0.6.11-staging.9c4dc38"
113
116
  },
114
117
  "optionalDependencies": {
115
118
  "@phosphor-icons/react": "^2.1.5",
116
119
  "react": "^18.0.0",
117
120
  "react-dom": "^18.0.0",
118
- "@dxos/react-ui": "0.6.10",
119
- "@dxos/react-ui-mosaic": "0.6.10",
120
- "@dxos/react-ui-theme": "0.6.10"
121
+ "@dxos/react-ui": "0.6.11-staging.9c4dc38",
122
+ "@dxos/react-ui-mosaic": "0.6.11-staging.9c4dc38",
123
+ "@dxos/react-ui-theme": "0.6.11-staging.9c4dc38"
121
124
  },
122
125
  "publishConfig": {
123
126
  "access": "public"
@@ -107,11 +107,14 @@ export const ScriptPlugin = (): PluginDefinition<ScriptPluginProvides> => {
107
107
  creators: [
108
108
  {
109
109
  id: 'create-stack-section-script',
110
- testId: 'scriptPlugin.createSectionSpaceScript',
110
+ testId: 'scriptPlugin.createSection',
111
111
  type: ['plugin name', { ns: SCRIPT_PLUGIN }],
112
112
  label: ['create stack section label', { ns: SCRIPT_PLUGIN }],
113
113
  icon: (props: any) => <Code {...props} />,
114
- intent: { plugin: SCRIPT_PLUGIN, action: ScriptAction.CREATE },
114
+ intent: {
115
+ plugin: SCRIPT_PLUGIN,
116
+ action: ScriptAction.CREATE,
117
+ },
115
118
  },
116
119
  ],
117
120
  },
@@ -146,8 +149,8 @@ const example = [
146
149
  'export default async ({ ',
147
150
  ' event: { data: { request } },',
148
151
  ' context: { space, ai } ',
149
- '}) => {',
150
- " return new Response('Hello World, from ScriptPlugin!');",
152
+ '}: any) => {',
153
+ " return new Response('Hello DXOS!');",
151
154
  '};',
152
155
  '',
153
156
  ].join('\n');
@@ -13,24 +13,27 @@ import {
13
13
  type ThemedClassName,
14
14
  Toolbar,
15
15
  useControlledValue,
16
- useDynamicRef,
17
16
  useTranslation,
18
17
  } from '@dxos/react-ui';
18
+ import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
19
19
  import { mx } from '@dxos/react-ui-theme';
20
20
 
21
21
  import { SCRIPT_PLUGIN } from '../meta';
22
22
 
23
23
  type State = 'pending' | 'responding';
24
24
 
25
- type Item = { type: 'request' | 'response'; text: string };
25
+ /**
26
+ * Request or response.
27
+ */
28
+ type Message = { type: 'request' | 'response'; text?: string; data?: any; error?: Error };
26
29
 
27
- export type DetailsPanelProps = ThemedClassName<{
30
+ export type DebugPanelProps = ThemedClassName<{
28
31
  functionUrl?: string;
29
32
  binding?: string;
30
33
  onBindingChange?: (binding: string) => void;
31
34
  }>;
32
35
 
33
- export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBindingChange }: DetailsPanelProps) => {
36
+ export const DebugPanel = ({ classNames, functionUrl, binding: _binding, onBindingChange }: DebugPanelProps) => {
34
37
  const { t } = useTranslation(SCRIPT_PLUGIN);
35
38
 
36
39
  const bindingInputRef = useRef<HTMLInputElement>(null);
@@ -42,11 +45,10 @@ export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBin
42
45
  const inputRef = useRef<HTMLInputElement>(null);
43
46
  const [input, setInput] = useState('');
44
47
  const [result, setResult] = useState('');
45
- const resultRef = useDynamicRef(result);
46
48
  const [state, setState] = useState<State | null>(null);
47
49
 
48
50
  // TODO(burdon): Persistent history (non-space ECHO data?)
49
- const [history, setHistory] = useState<Item[]>([]);
51
+ const [history, setHistory] = useState<Message[]>([]);
50
52
 
51
53
  const scrollerRef = useRef<HTMLDivElement>(null);
52
54
  const handleScroll = () => {
@@ -67,7 +69,6 @@ export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBin
67
69
  controller.current = undefined;
68
70
  };
69
71
 
70
- // TODO(burdon): Halt streaming request?
71
72
  const handleClear = () => {
72
73
  handleStop();
73
74
  setInput('');
@@ -76,52 +77,81 @@ export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBin
76
77
  inputRef.current?.focus();
77
78
  };
78
79
 
79
- const handleDone = () => {
80
+ const handleResponse = ({ text, data, error }: { text?: string; data?: any; error?: Error } = {}) => {
80
81
  controller.current = undefined;
81
- setHistory((history) => [...history, { type: 'response', text: resultRef.current }]);
82
+ setHistory((history) => [...history, { type: 'response', text, data, error } satisfies Message]);
82
83
  setResult('');
83
84
  setState(null);
84
85
  handleScroll();
85
86
  };
86
87
 
87
- const handleRun = async (input: string) => {
88
- if (input.trim().length === 0) {
89
- inputRef.current?.focus();
90
- return;
91
- }
92
-
93
- setHistory((history) => [...history, { type: 'request', text: input }]);
94
- setInput('');
95
- setResult('');
96
-
88
+ const handleRequest = async (input: string) => {
97
89
  // Returns a promise that resolves when a value has been received.
98
90
  try {
99
91
  setState('pending');
100
92
 
93
+ let data = null;
94
+ // Detect JSON input.
95
+ if (input.charAt(0) === '{') {
96
+ try {
97
+ const validJsonString = input.replace(/'/g, '"').replace(/([{,]\s*)([a-zA-Z0-9_]+)(\s*:)/g, '$1"$2"$3');
98
+ data = JSON.parse(validJsonString);
99
+ input = JSON.stringify(data, null, 2);
100
+ } catch (err) {
101
+ inputRef.current?.focus();
102
+ return;
103
+ }
104
+ }
105
+
106
+ setInput('');
107
+ setResult('');
108
+ setHistory((history) => [...history, { type: 'request', text: input }]);
109
+ setTimeout(() => {
110
+ handleScroll();
111
+ });
112
+
101
113
  // Throws DOMException when aborted.
102
114
  controller.current = new AbortController();
103
115
  const response = await fetch(functionUrl!, {
104
116
  signal: controller.current.signal,
105
117
  method: 'POST',
106
118
  body: input,
119
+ headers: {
120
+ 'Content-Type': data ? 'application/json' : 'text/plain',
121
+ },
107
122
  });
108
123
 
109
- // TODO(burdon): Error handling.
124
+ // Check for error.
110
125
  if (!response.ok) {
111
- setState(null);
126
+ const text = await response.text();
127
+ handleResponse({ error: new Error(text || response.statusText || String(response.status)) });
128
+ return;
129
+ }
130
+
131
+ // Check for JSON.
132
+ const contentType = response.headers.get('content-type');
133
+ if (contentType === 'application/json') {
134
+ const data = await response.json();
135
+ handleResponse({ data });
112
136
  return;
113
137
  }
114
138
 
139
+ // Text (including streaming).
115
140
  setState('responding');
141
+ let text = '';
116
142
  const reader = response.body!.getReader();
117
143
  const pump = async ({ done, value }: ReadableStreamReadResult<Uint8Array>): Promise<void> => {
118
- setResult((result) => result + new TextDecoder().decode(value));
119
- handleScroll();
144
+ text = text + new TextDecoder().decode(value);
120
145
  if (!controller.current || done) {
121
- handleDone();
146
+ handleResponse({ text });
122
147
  return;
123
148
  }
124
149
 
150
+ setResult(text);
151
+ setTimeout(() => {
152
+ handleScroll();
153
+ });
154
+
125
155
  return reader.read().then(pump);
126
156
  };
127
157
 
@@ -133,10 +163,13 @@ export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBin
133
163
  log.catch(err);
134
164
  }
135
165
  });
136
- } catch (err) {
137
- setState(null);
166
+ } catch (err: any) {
138
167
  if (err !== 'stop') {
168
+ const error = err instanceof Error ? err : new Error(err);
169
+ handleResponse({ error });
139
170
  log.catch(err);
171
+ } else {
172
+ handleResponse();
140
173
  }
141
174
  }
142
175
  };
@@ -157,7 +190,7 @@ export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBin
157
190
  placeholder={t('function request placeholder')}
158
191
  value={input}
159
192
  onChange={(ev) => setInput(ev.target.value)}
160
- onKeyDown={(ev) => ev.key === 'Enter' && handleRun(input)}
193
+ onKeyDown={(ev) => ev.key === 'Enter' && handleRequest(input)}
161
194
  />
162
195
  </Input.Root>
163
196
  <Input.Root>
@@ -170,7 +203,7 @@ export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBin
170
203
  onBlur={handleBlur}
171
204
  />
172
205
  </Input.Root>
173
- <Toolbar.Button onClick={() => handleRun(input)}>
206
+ <Toolbar.Button onClick={() => handleRequest(input)}>
174
207
  <Icon icon='ph--play--regular' size={4} />
175
208
  </Toolbar.Button>
176
209
  <Toolbar.Button onClick={() => (state ? handleStop() : handleClear())}>
@@ -190,17 +223,35 @@ const RobotAvatar = () => (
190
223
  </Avatar.Root>
191
224
  );
192
225
 
193
- // TODO(burdon): Replace with thread.
194
- // TODO(burdon): Delete individual messages.
195
- // TODO(burdon): Re-run question.
196
- const MessageThread = ({ state, result, history }: { state: State | null; result: string; history: Item[] }) => {
226
+ const MessageItem = ({ classNames, message }: ThemedClassName<{ message: Message }>) => {
227
+ const { text, data, error } = message;
228
+ if (error) {
229
+ return <span className={mx(classNames, 'whitespace-pre text-error')}>{String(error)}</span>;
230
+ } else if (data) {
231
+ return (
232
+ <SyntaxHighlighter language='json' className={mx('text-xs', classNames)}>
233
+ {JSON.stringify(data, null, 2)}
234
+ </SyntaxHighlighter>
235
+ );
236
+ }
237
+
238
+ return <span className={mx(classNames)}>{text || '\u00D8'}</span>;
239
+ };
240
+
241
+ // TODO(burdon): Replace with thread?
242
+ // TODO(burdon): Button to delete individual messages.
243
+ // TODO(burdon): Button to re-run question.
244
+ const MessageThread = ({ state, result, history }: { state: State | null; result: string; history: Message[] }) => {
197
245
  return (
198
246
  <>
199
- {history.map(({ type, text }, i) => (
247
+ {history.map((message, i) => (
200
248
  <div key={i} className='grid grid-cols-[3rem_1fr]'>
201
- <div>{type === 'response' && <RobotAvatar />}</div>
202
- <div className={mx('flex', type === 'request' && 'justify-end')}>
203
- <span className={mx(type === 'request' && 'p-1 px-2 rounded-lg bg-hoverSurface')}>{text}</span>
249
+ <div>{message.type === 'response' && <RobotAvatar />}</div>
250
+ <div className={mx('flex', message.type === 'request' && 'justify-end')}>
251
+ <MessageItem
252
+ classNames={[message.type === 'request' && 'p-1 px-2 rounded-lg bg-hoverSurface']}
253
+ message={message}
254
+ />
204
255
  </div>
205
256
  </div>
206
257
  ))}
@@ -3,15 +3,14 @@
3
3
  //
4
4
 
5
5
  import '@dxos-theme';
6
-
7
6
  import React, { useEffect, useMemo } from 'react';
8
7
 
9
8
  import { createSpaceObjectGenerator, TestSchemaType } from '@dxos/echo-generator';
10
9
  import { TextType } from '@dxos/plugin-markdown/types';
11
10
  import { useClient } from '@dxos/react-client';
12
- import { create, createDocAccessor, createEchoObject } from '@dxos/react-client/echo';
13
- import { ClientRepeater } from '@dxos/react-client/testing';
14
- import { withTheme } from '@dxos/storybook-utils';
11
+ import { create, createEchoObject, useSpaces } from '@dxos/react-client/echo';
12
+ import { withClientProvider } from '@dxos/react-client/testing';
13
+ import { withLayout, withTheme } from '@dxos/storybook-utils';
15
14
 
16
15
  // @ts-ignore
17
16
  import mainUrl from './FrameContainer/frame?url';
@@ -32,33 +31,41 @@ const code = [
32
31
 
33
32
  const Story = () => {
34
33
  const client = useClient();
35
- // TODO(dmaretskyi): Review what's the right way to create automerge-backed objects.
34
+ const spaces = useSpaces();
35
+ console.log(spaces.length);
36
+
37
+ useEffect(() => {
38
+ try {
39
+ // TODO(burdon): Default not set.
40
+ const generator = createSpaceObjectGenerator(client.spaces.default);
41
+ generator.addSchemas();
42
+ void generator
43
+ .createObjects({
44
+ [TestSchemaType.organization]: 20,
45
+ [TestSchemaType.contact]: 50,
46
+ })
47
+ .catch();
48
+ } catch (err) {
49
+ console.log(err);
50
+ }
51
+ }, [client]);
52
+
53
+ // TODO(burdon): Review what's the right way to create automerge-backed objects.
36
54
  const object = useMemo(
37
55
  () => createEchoObject(create(ScriptType, { source: create(TextType, { content: code }) })),
38
- [code],
56
+ [],
39
57
  );
40
- const accessor = useMemo(() => createDocAccessor(object, ['content']), [object]);
41
- useEffect(() => {
42
- const generator = createSpaceObjectGenerator(client.spaces.default);
43
- generator.addSchemas();
44
- void generator.createObjects({ [TestSchemaType.organization]: 20, [TestSchemaType.contact]: 50 }).catch();
45
- }, []);
46
58
 
47
59
  // TODO(dmaretskyi): Not sure how to provide `containerUrl` here since the html now lives in composer-app.
48
60
  // TODO(burdon): Normalize html/frame.tsx with composer-app to test locally.
49
- return (
50
- <div className={'flex fixed inset-0'}>{accessor && <ScriptBlock script={object} containerUrl={mainUrl} />}</div>
51
- );
61
+ return <ScriptBlock script={object} containerUrl={mainUrl} />;
52
62
  };
53
63
 
54
64
  export default {
55
65
  title: 'plugin-script/ScriptBlock',
56
66
  component: ScriptBlock,
57
- render: () => <ClientRepeater component={Story} createSpace />,
58
- decorators: [withTheme],
59
- parameters: {
60
- layout: 'fullscreen',
61
- },
67
+ decorators: [withTheme, withLayout({ fullscreen: true, tooltips: true }), withClientProvider({ createSpace: true })],
68
+ render: Story,
62
69
  };
63
70
 
64
71
  export const Default = {};
@@ -10,13 +10,22 @@ import React, { useCallback, useMemo, useState } from 'react';
10
10
 
11
11
  import { log } from '@dxos/log';
12
12
  import { useClient } from '@dxos/react-client';
13
- import { create, createDocAccessor, Filter, getMeta, getSpace, useQuery } from '@dxos/react-client/echo';
13
+ import {
14
+ create,
15
+ createDocAccessor,
16
+ Filter,
17
+ fullyQualifiedId,
18
+ getMeta,
19
+ getSpace,
20
+ useQuery,
21
+ } from '@dxos/react-client/echo';
14
22
  import { useIdentity } from '@dxos/react-client/halo';
15
23
  import { useTranslation } from '@dxos/react-ui';
24
+ import { useHasAttention } from '@dxos/react-ui-attention';
16
25
  import { createDataExtensions, listener } from '@dxos/react-ui-editor';
17
26
  import { mx } from '@dxos/react-ui-theme';
18
27
 
19
- import { DetailsPanel } from './DetailsPanel';
28
+ import { DebugPanel } from './DebugPanel';
20
29
  import { Toolbar } from './Toolbar';
21
30
  import { TypescriptEditor, type TypescriptEditorProps } from './TypescriptEditor';
22
31
  import { Bundler } from '../bundler';
@@ -36,6 +45,7 @@ export type ScriptEditorProps = {
36
45
 
37
46
  export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
38
47
  const { t } = useTranslation(SCRIPT_PLUGIN);
48
+ const hasAttention = useHasAttention(fullyQualifiedId(script));
39
49
  const client = useClient();
40
50
  const identity = useIdentity();
41
51
  const space = getSpace(script);
@@ -157,17 +167,22 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
157
167
  return (
158
168
  <div
159
169
  role='none'
160
- className={mx(role === 'article' && 'row-span-2', 'flex flex-col is-full bs-full overflow-hidden')}
170
+ className={mx(
171
+ 'flex flex-col is-full bs-full overflow-hidden',
172
+ role === 'article' && 'row-span-2',
173
+ hasAttention && 'attention-surface',
174
+ )}
161
175
  >
162
176
  <TypescriptEditor
163
177
  id={script.id}
164
178
  env={env}
165
179
  initialValue={initialValue}
166
180
  extensions={extensions}
167
- className='flex is-full bs-full overflow-hidden border-b border-separator'
181
+ className='flex is-full bs-full overflow-hidden ch-focus-ring-inset-over-all'
168
182
  />
169
183
 
170
184
  <Toolbar
185
+ classNames='border-t border-separator'
171
186
  deployed={Boolean(existingFunctionUrl) && !script.changed}
172
187
  functionUrl={functionUrl}
173
188
  error={error}
@@ -178,7 +193,7 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
178
193
  />
179
194
 
180
195
  {showPanel && (
181
- <DetailsPanel functionUrl={functionUrl} binding={fn?.binding} onBindingChange={handleBindingChange} />
196
+ <DebugPanel functionUrl={functionUrl} binding={fn?.binding} onBindingChange={handleBindingChange} />
182
197
  )}
183
198
  </div>
184
199
  );
@@ -4,16 +4,14 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- // import React from 'react';
7
+ import { withLayout, withTheme } from '@dxos/storybook-utils';
8
8
 
9
9
  import { Toolbar } from './Toolbar';
10
10
 
11
11
  export default {
12
12
  title: 'plugin-script/Toolbar',
13
13
  component: Toolbar,
14
- parameters: {
15
- layout: 'fullscreen',
16
- },
14
+ decorators: [withTheme, withLayout({ tooltips: true })],
17
15
  };
18
16
 
19
17
  export const Default = { args: { binding: 'example', deployed: true } };