@dxos/plugin-script 0.6.10 → 0.6.11-staging.a542fc9

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 (40) hide show
  1. package/dist/lib/browser/{ScriptEditor-WNCGR5OU.mjs → ScriptEditor-C6MMOFDI.mjs} +121 -59
  2. package/dist/lib/browser/ScriptEditor-C6MMOFDI.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-ZGCETT2W.mjs → chunk-O4LWBX6A.mjs} +7 -2
  4. package/dist/lib/browser/chunk-O4LWBX6A.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +5 -5
  6. package/dist/lib/browser/index.mjs.map +2 -2
  7. package/dist/lib/browser/meta.json +1 -1
  8. package/dist/lib/browser/types/index.mjs +1 -1
  9. package/dist/lib/node/{ScriptEditor-RR7AVOQL.cjs → ScriptEditor-XQESJORL.cjs} +136 -74
  10. package/dist/lib/node/ScriptEditor-XQESJORL.cjs.map +7 -0
  11. package/dist/lib/node/{chunk-VW2POILW.cjs → chunk-5RBVXKXT.cjs} +9 -4
  12. package/dist/lib/node/chunk-5RBVXKXT.cjs.map +7 -0
  13. package/dist/lib/node/index.cjs +17 -17
  14. package/dist/lib/node/index.cjs.map +2 -2
  15. package/dist/lib/node/meta.json +1 -1
  16. package/dist/lib/node/types/index.cjs +4 -4
  17. package/dist/lib/node/types/index.cjs.map +1 -1
  18. package/dist/types/src/ScriptPlugin.d.ts.map +1 -1
  19. package/dist/types/src/components/DebugPanel.d.ts +9 -0
  20. package/dist/types/src/components/DebugPanel.d.ts.map +1 -0
  21. package/dist/types/src/components/Toolbar.d.ts.map +1 -1
  22. package/dist/types/src/types/script.d.ts +12 -0
  23. package/dist/types/src/types/script.d.ts.map +1 -1
  24. package/dist/types/src/types/types.d.ts.map +1 -1
  25. package/package.json +34 -33
  26. package/src/ScriptPlugin.tsx +7 -4
  27. package/src/components/{DetailsPanel.tsx → DebugPanel.tsx} +101 -36
  28. package/src/components/ScriptEditor.tsx +2 -2
  29. package/src/components/Toolbar.tsx +14 -29
  30. package/src/types/script.ts +13 -0
  31. package/dist/lib/browser/ScriptEditor-WNCGR5OU.mjs.map +0 -7
  32. package/dist/lib/browser/chunk-ZGCETT2W.mjs.map +0 -7
  33. package/dist/lib/node/ScriptEditor-RR7AVOQL.cjs.map +0 -7
  34. package/dist/lib/node/chunk-VW2POILW.cjs.map +0 -7
  35. package/dist/types/src/components/DetailsPanel.d.ts +0 -9
  36. package/dist/types/src/components/DetailsPanel.d.ts.map +0 -1
  37. package/dist/types/src/data/templates.md.d.ts +0 -3
  38. package/dist/types/src/data/templates.md.d.ts.map +0 -1
  39. package/src/data/templates.md.ts +0 -44
  40. /package/src/types/{types.tsx → types.ts} +0 -0
@@ -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.a542fc9",
4
4
  "description": "Braneframe notebook plugin",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -71,30 +71,31 @@
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.a542fc9",
77
+ "@dxos/client-protocol": "0.6.11-staging.a542fc9",
78
+ "@dxos/crypto": "0.6.11-staging.a542fc9",
79
+ "@dxos/client": "0.6.11-staging.a542fc9",
80
+ "@dxos/echo-schema": "0.6.11-staging.a542fc9",
81
+ "@dxos/invariant": "0.6.11-staging.a542fc9",
82
+ "@dxos/keys": "0.6.11-staging.a542fc9",
83
+ "@dxos/async": "0.6.11-staging.a542fc9",
84
+ "@dxos/debug": "0.6.11-staging.a542fc9",
85
+ "@dxos/log": "0.6.11-staging.a542fc9",
86
+ "@dxos/plugin-client": "0.6.11-staging.a542fc9",
87
+ "@dxos/plugin-explorer": "0.6.11-staging.a542fc9",
88
+ "@dxos/plugin-graph": "0.6.11-staging.a542fc9",
89
+ "@dxos/plugin-space": "0.6.11-staging.a542fc9",
90
+ "@dxos/plugin-markdown": "0.6.11-staging.a542fc9",
91
+ "@dxos/plugin-stack": "0.6.11-staging.a542fc9",
92
+ "@dxos/plugin-theme": "0.6.11-staging.a542fc9",
93
+ "@dxos/protocols": "0.6.11-staging.a542fc9",
94
+ "@dxos/react-client": "0.6.11-staging.a542fc9",
95
+ "@dxos/react-ui-editor": "0.6.11-staging.a542fc9",
96
+ "@dxos/rpc": "0.6.11-staging.a542fc9",
97
+ "@dxos/util": "0.6.11-staging.a542fc9",
98
+ "@dxos/rpc-tunnel": "0.6.11-staging.a542fc9"
98
99
  },
99
100
  "devDependencies": {
100
101
  "@phosphor-icons/react": "^2.1.5",
@@ -104,20 +105,20 @@
104
105
  "react": "~18.2.0",
105
106
  "react-dom": "~18.2.0",
106
107
  "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"
108
+ "@dxos/echo-generator": "0.6.11-staging.a542fc9",
109
+ "@dxos/random": "0.6.11-staging.a542fc9",
110
+ "@dxos/react-ui": "0.6.11-staging.a542fc9",
111
+ "@dxos/react-ui-mosaic": "0.6.11-staging.a542fc9",
112
+ "@dxos/storybook-utils": "0.6.11-staging.a542fc9",
113
+ "@dxos/react-ui-theme": "0.6.11-staging.a542fc9"
113
114
  },
114
115
  "optionalDependencies": {
115
116
  "@phosphor-icons/react": "^2.1.5",
116
117
  "react": "^18.0.0",
117
118
  "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"
119
+ "@dxos/react-ui": "0.6.11-staging.a542fc9",
120
+ "@dxos/react-ui-mosaic": "0.6.11-staging.a542fc9",
121
+ "@dxos/react-ui-theme": "0.6.11-staging.a542fc9"
121
122
  },
122
123
  "publishConfig": {
123
124
  "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');
@@ -3,6 +3,15 @@
3
3
  //
4
4
 
5
5
  import React, { useEffect, useRef, useState } from 'react';
6
+ // @ts-ignore
7
+ // eslint-disable-next-line no-restricted-imports
8
+ import SyntaxHighlighter from 'react-syntax-highlighter';
9
+ // @ts-ignore
10
+ // eslint-disable-next-line no-restricted-imports
11
+ import styleDark from 'react-syntax-highlighter/dist/esm/styles/hljs/a11y-dark';
12
+ // @ts-ignore
13
+ // eslint-disable-next-line no-restricted-imports
14
+ import styleLight from 'react-syntax-highlighter/dist/esm/styles/hljs/a11y-light';
6
15
 
7
16
  import { log } from '@dxos/log';
8
17
  import {
@@ -13,7 +22,7 @@ import {
13
22
  type ThemedClassName,
14
23
  Toolbar,
15
24
  useControlledValue,
16
- useDynamicRef,
25
+ useThemeContext,
17
26
  useTranslation,
18
27
  } from '@dxos/react-ui';
19
28
  import { mx } from '@dxos/react-ui-theme';
@@ -22,15 +31,18 @@ import { SCRIPT_PLUGIN } from '../meta';
22
31
 
23
32
  type State = 'pending' | 'responding';
24
33
 
25
- type Item = { type: 'request' | 'response'; text: string };
34
+ /**
35
+ * Request or response.
36
+ */
37
+ type Message = { type: 'request' | 'response'; text?: string; data?: any; error?: Error };
26
38
 
27
- export type DetailsPanelProps = ThemedClassName<{
39
+ export type DebugPanelProps = ThemedClassName<{
28
40
  functionUrl?: string;
29
41
  binding?: string;
30
42
  onBindingChange?: (binding: string) => void;
31
43
  }>;
32
44
 
33
- export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBindingChange }: DetailsPanelProps) => {
45
+ export const DebugPanel = ({ classNames, functionUrl, binding: _binding, onBindingChange }: DebugPanelProps) => {
34
46
  const { t } = useTranslation(SCRIPT_PLUGIN);
35
47
 
36
48
  const bindingInputRef = useRef<HTMLInputElement>(null);
@@ -42,11 +54,10 @@ export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBin
42
54
  const inputRef = useRef<HTMLInputElement>(null);
43
55
  const [input, setInput] = useState('');
44
56
  const [result, setResult] = useState('');
45
- const resultRef = useDynamicRef(result);
46
57
  const [state, setState] = useState<State | null>(null);
47
58
 
48
59
  // TODO(burdon): Persistent history (non-space ECHO data?)
49
- const [history, setHistory] = useState<Item[]>([]);
60
+ const [history, setHistory] = useState<Message[]>([]);
50
61
 
51
62
  const scrollerRef = useRef<HTMLDivElement>(null);
52
63
  const handleScroll = () => {
@@ -67,7 +78,6 @@ export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBin
67
78
  controller.current = undefined;
68
79
  };
69
80
 
70
- // TODO(burdon): Halt streaming request?
71
81
  const handleClear = () => {
72
82
  handleStop();
73
83
  setInput('');
@@ -76,52 +86,81 @@ export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBin
76
86
  inputRef.current?.focus();
77
87
  };
78
88
 
79
- const handleDone = () => {
89
+ const handleResponse = ({ text, data, error }: { text?: string; data?: any; error?: Error } = {}) => {
80
90
  controller.current = undefined;
81
- setHistory((history) => [...history, { type: 'response', text: resultRef.current }]);
91
+ setHistory((history) => [...history, { type: 'response', text, data, error } satisfies Message]);
82
92
  setResult('');
83
93
  setState(null);
84
94
  handleScroll();
85
95
  };
86
96
 
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
-
97
+ const handleRequest = async (input: string) => {
97
98
  // Returns a promise that resolves when a value has been received.
98
99
  try {
99
100
  setState('pending');
100
101
 
102
+ let data = null;
103
+ // Detect JSON input.
104
+ if (input.charAt(0) === '{') {
105
+ try {
106
+ const validJsonString = input.replace(/'/g, '"').replace(/([{,]\s*)([a-zA-Z0-9_]+)(\s*:)/g, '$1"$2"$3');
107
+ data = JSON.parse(validJsonString);
108
+ input = JSON.stringify(data, null, 2);
109
+ } catch (err) {
110
+ inputRef.current?.focus();
111
+ return;
112
+ }
113
+ }
114
+
115
+ setInput('');
116
+ setResult('');
117
+ setHistory((history) => [...history, { type: 'request', text: input }]);
118
+ setTimeout(() => {
119
+ handleScroll();
120
+ });
121
+
101
122
  // Throws DOMException when aborted.
102
123
  controller.current = new AbortController();
103
124
  const response = await fetch(functionUrl!, {
104
125
  signal: controller.current.signal,
105
126
  method: 'POST',
106
127
  body: input,
128
+ headers: {
129
+ 'Content-Type': data ? 'application/json' : 'text/plain',
130
+ },
107
131
  });
108
132
 
109
- // TODO(burdon): Error handling.
133
+ // Check for error.
110
134
  if (!response.ok) {
111
- setState(null);
135
+ const text = await response.text();
136
+ handleResponse({ error: new Error(text || response.statusText || String(response.status)) });
137
+ return;
138
+ }
139
+
140
+ // Check for JSON.
141
+ const contentType = response.headers.get('content-type');
142
+ if (contentType === 'application/json') {
143
+ const data = await response.json();
144
+ handleResponse({ data });
112
145
  return;
113
146
  }
114
147
 
148
+ // Text (including streaming).
115
149
  setState('responding');
150
+ let text = '';
116
151
  const reader = response.body!.getReader();
117
152
  const pump = async ({ done, value }: ReadableStreamReadResult<Uint8Array>): Promise<void> => {
118
- setResult((result) => result + new TextDecoder().decode(value));
119
- handleScroll();
153
+ text = text + new TextDecoder().decode(value);
120
154
  if (!controller.current || done) {
121
- handleDone();
155
+ handleResponse({ text });
122
156
  return;
123
157
  }
124
158
 
159
+ setResult(text);
160
+ setTimeout(() => {
161
+ handleScroll();
162
+ });
163
+
125
164
  return reader.read().then(pump);
126
165
  };
127
166
 
@@ -133,10 +172,13 @@ export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBin
133
172
  log.catch(err);
134
173
  }
135
174
  });
136
- } catch (err) {
137
- setState(null);
175
+ } catch (err: any) {
138
176
  if (err !== 'stop') {
177
+ const error = err instanceof Error ? err : new Error(err);
178
+ handleResponse({ error });
139
179
  log.catch(err);
180
+ } else {
181
+ handleResponse();
140
182
  }
141
183
  }
142
184
  };
@@ -157,7 +199,7 @@ export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBin
157
199
  placeholder={t('function request placeholder')}
158
200
  value={input}
159
201
  onChange={(ev) => setInput(ev.target.value)}
160
- onKeyDown={(ev) => ev.key === 'Enter' && handleRun(input)}
202
+ onKeyDown={(ev) => ev.key === 'Enter' && handleRequest(input)}
161
203
  />
162
204
  </Input.Root>
163
205
  <Input.Root>
@@ -170,7 +212,7 @@ export const DetailsPanel = ({ classNames, functionUrl, binding: _binding, onBin
170
212
  onBlur={handleBlur}
171
213
  />
172
214
  </Input.Root>
173
- <Toolbar.Button onClick={() => handleRun(input)}>
215
+ <Toolbar.Button onClick={() => handleRequest(input)}>
174
216
  <Icon icon='ph--play--regular' size={4} />
175
217
  </Toolbar.Button>
176
218
  <Toolbar.Button onClick={() => (state ? handleStop() : handleClear())}>
@@ -190,17 +232,40 @@ const RobotAvatar = () => (
190
232
  </Avatar.Root>
191
233
  );
192
234
 
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[] }) => {
235
+ const MessageItem = ({ classNames, message }: ThemedClassName<{ message: Message }>) => {
236
+ const { themeMode } = useThemeContext();
237
+ const { text, data, error } = message;
238
+ if (error) {
239
+ return <span className={mx(classNames, 'whitespace-pre text-error')}>{String(error)}</span>;
240
+ } else if (data) {
241
+ return (
242
+ <SyntaxHighlighter
243
+ language='json'
244
+ style={themeMode === 'dark' ? styleDark : styleLight}
245
+ className={mx('w-full text-xs', classNames)}
246
+ >
247
+ {JSON.stringify(data, null, 2)}
248
+ </SyntaxHighlighter>
249
+ );
250
+ }
251
+
252
+ return <span className={mx(classNames)}>{text || '\u00D8'}</span>;
253
+ };
254
+
255
+ // TODO(burdon): Replace with thread?
256
+ // TODO(burdon): Button to delete individual messages.
257
+ // TODO(burdon): Button to re-run question.
258
+ const MessageThread = ({ state, result, history }: { state: State | null; result: string; history: Message[] }) => {
197
259
  return (
198
260
  <>
199
- {history.map(({ type, text }, i) => (
261
+ {history.map((message, i) => (
200
262
  <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>
263
+ <div>{message.type === 'response' && <RobotAvatar />}</div>
264
+ <div className={mx('flex', message.type === 'request' && 'justify-end')}>
265
+ <MessageItem
266
+ classNames={[message.type === 'request' && 'p-1 px-2 rounded-lg bg-hoverSurface']}
267
+ message={message}
268
+ />
204
269
  </div>
205
270
  </div>
206
271
  ))}
@@ -16,7 +16,7 @@ import { useTranslation } from '@dxos/react-ui';
16
16
  import { createDataExtensions, listener } from '@dxos/react-ui-editor';
17
17
  import { mx } from '@dxos/react-ui-theme';
18
18
 
19
- import { DetailsPanel } from './DetailsPanel';
19
+ import { DebugPanel } from './DebugPanel';
20
20
  import { Toolbar } from './Toolbar';
21
21
  import { TypescriptEditor, type TypescriptEditorProps } from './TypescriptEditor';
22
22
  import { Bundler } from '../bundler';
@@ -178,7 +178,7 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
178
178
  />
179
179
 
180
180
  {showPanel && (
181
- <DetailsPanel functionUrl={functionUrl} binding={fn?.binding} onBindingChange={handleBindingChange} />
181
+ <DebugPanel functionUrl={functionUrl} binding={fn?.binding} onBindingChange={handleBindingChange} />
182
182
  )}
183
183
  </div>
184
184
  );
@@ -2,7 +2,6 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Checks, CircleNotch, Link, MagicWand, UploadSimple, WarningCircle } from '@phosphor-icons/react';
6
5
  import React, { useState } from 'react';
7
6
 
8
7
  import {
@@ -14,7 +13,6 @@ import {
14
13
  Tooltip,
15
14
  useTranslation,
16
15
  } from '@dxos/react-ui';
17
- import { getSize, mx } from '@dxos/react-ui-theme';
18
16
 
19
17
  import { SCRIPT_PLUGIN } from '../meta';
20
18
 
@@ -64,7 +62,7 @@ export const Toolbar = ({
64
62
  <Tooltip.Root>
65
63
  <Tooltip.Trigger asChild>
66
64
  <NaturalToolbar.Button variant='ghost' onClick={onFormat}>
67
- <MagicWand className={getSize(4)} />
65
+ <Icon icon='ph--magic-want--regular' size={4} />
68
66
  </NaturalToolbar.Button>
69
67
  </Tooltip.Trigger>
70
68
  <Tooltip.Portal>
@@ -78,27 +76,10 @@ export const Toolbar = ({
78
76
 
79
77
  <div role='separator' className='grow' />
80
78
 
81
- {functionUrl && (
82
- <Tooltip.Root>
83
- <Tooltip.Trigger asChild>
84
- <NaturalToolbar.Button variant='ghost' onClick={handleCopyLink}>
85
- <Link className={getSize(4)} />
86
- </NaturalToolbar.Button>
87
- </Tooltip.Trigger>
88
- <Tooltip.Portal>
89
- <Tooltip.Content>
90
- <Tooltip.Arrow />
91
- {t('copy link label')}
92
- </Tooltip.Content>
93
- </Tooltip.Portal>
94
- </Tooltip.Root>
95
- )}
96
-
97
- {/* TODO(wittjosiah): Better treatment for status indicators in toolbar? */}
98
79
  {error ? (
99
80
  <Tooltip.Root>
100
81
  <Tooltip.Trigger>
101
- <WarningCircle className={getSize(4)} />
82
+ <Icon icon='ph--warning-circle--regular' size={4} />
102
83
  </Tooltip.Trigger>
103
84
  <Tooltip.Portal>
104
85
  <Tooltip.Content>
@@ -107,27 +88,31 @@ export const Toolbar = ({
107
88
  </Tooltip.Content>
108
89
  </Tooltip.Portal>
109
90
  </Tooltip.Root>
110
- ) : deployed ? (
91
+ ) : null}
92
+
93
+ {functionUrl && deployed && (
111
94
  <Tooltip.Root>
112
- <Tooltip.Trigger>
113
- <Checks className={getSize(4)} />
95
+ <Tooltip.Trigger asChild>
96
+ <NaturalToolbar.Button variant='ghost' onClick={handleCopyLink}>
97
+ <Icon icon='ph--link--regular' size={4} />
98
+ </NaturalToolbar.Button>
114
99
  </Tooltip.Trigger>
115
100
  <Tooltip.Portal>
116
101
  <Tooltip.Content>
117
102
  <Tooltip.Arrow />
118
- {t('deployed label')}
103
+ {t('copy link label')}
119
104
  </Tooltip.Content>
120
105
  </Tooltip.Portal>
121
106
  </Tooltip.Root>
122
- ) : null}
107
+ )}
123
108
 
124
109
  <Tooltip.Root>
125
110
  <Tooltip.Trigger asChild>
126
111
  <NaturalToolbar.Button variant='ghost' onClick={handleDeploy} disabled={pending}>
127
112
  {pending ? (
128
- <CircleNotch className={mx('spinner', getSize(4))} />
113
+ <Icon icon='ph--spinner--regular' size={4} classNames='animate-spin-slow' />
129
114
  ) : (
130
- <UploadSimple className={getSize(4)} />
115
+ <Icon icon='ph--cloud-arrow-up--regular' size={4} />
131
116
  )}
132
117
  </NaturalToolbar.Button>
133
118
  </Tooltip.Trigger>
@@ -143,7 +128,7 @@ export const Toolbar = ({
143
128
  <Tooltip.Root>
144
129
  <Tooltip.Trigger asChild>
145
130
  <NaturalToolbar.Button variant='ghost' onClick={onTogglePanel}>
146
- <Icon icon={showPanel ? 'ph--caret-up--regular' : 'ph--caret-down--regular'} size={4} />
131
+ <Icon icon={showPanel ? 'ph--caret-down--regular' : 'ph--caret-up--regular'} size={4} />
147
132
  </NaturalToolbar.Button>
148
133
  </Tooltip.Trigger>
149
134
  <Tooltip.Portal>
@@ -5,16 +5,29 @@
5
5
  import { ref, S, TypedObject } from '@dxos/echo-schema';
6
6
  import { TextType } from '@dxos/plugin-markdown/types';
7
7
 
8
+ // TODO(burdon): Which is the outer type? I.e., in the navbar?
9
+
10
+ /**
11
+ * Source script.
12
+ */
8
13
  export class ScriptType extends TypedObject({ typename: 'dxos.org/type/Script', version: '0.1.0' })({
14
+ // TODO(burdon): Change to URI?
9
15
  name: S.optional(S.String),
16
+ description: S.optional(S.String),
10
17
  source: ref(TextType),
18
+ // TODO(burdon): Change to has of deployed content.
11
19
  // Whether source has changed since last deploy.
12
20
  changed: S.optional(S.Boolean),
13
21
  }) {}
14
22
 
23
+ /**
24
+ * Function deployment.
25
+ */
15
26
  export class FunctionType extends TypedObject({ typename: 'dxos.org/type/Function', version: '0.1.0' })({
27
+ name: S.optional(S.String),
16
28
  version: S.Number,
17
29
  // Local binding to a function name.
30
+ // TODO(burdon): Move binding to inner type.
18
31
  binding: S.optional(S.String),
19
32
  // Reference to a source script if it exists within ECHO.
20
33
  source: S.optional(ref(ScriptType)),