@dxos/plugin-script 0.6.13 → 0.6.14-main.2b6a0f3

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 (202) hide show
  1. package/README.md +1 -1
  2. package/dist/lib/browser/AutomationPanel-Y7LQYFOY.mjs +78 -0
  3. package/dist/lib/browser/AutomationPanel-Y7LQYFOY.mjs.map +7 -0
  4. package/dist/lib/browser/ScriptContainer-KGZGB5AY.mjs +423 -0
  5. package/dist/lib/browser/ScriptContainer-KGZGB5AY.mjs.map +7 -0
  6. package/dist/lib/browser/{chunk-RHEZ5LUU.mjs → chunk-C4AX55JL.mjs} +51 -2
  7. package/dist/lib/browser/{chunk-RHEZ5LUU.mjs.map → chunk-C4AX55JL.mjs.map} +4 -4
  8. package/dist/lib/browser/{chunk-DL5XQRQM.mjs → chunk-KHNN3F7J.mjs} +18 -6
  9. package/dist/lib/browser/chunk-KHNN3F7J.mjs.map +7 -0
  10. package/dist/lib/browser/{chunk-O4LWBX6A.mjs → chunk-KXVQPS4Y.mjs} +2 -2
  11. package/dist/lib/browser/{chunk-O4LWBX6A.mjs.map → chunk-KXVQPS4Y.mjs.map} +2 -2
  12. package/dist/lib/browser/chunk-SGQHRB4P.mjs +17 -0
  13. package/dist/lib/browser/chunk-SGQHRB4P.mjs.map +7 -0
  14. package/dist/lib/browser/chunk-SP3Q7BCM.mjs +248 -0
  15. package/dist/lib/browser/chunk-SP3Q7BCM.mjs.map +7 -0
  16. package/dist/lib/browser/edge/index.mjs +5 -1
  17. package/dist/lib/browser/index.mjs +86 -61
  18. package/dist/lib/browser/index.mjs.map +4 -4
  19. package/dist/lib/browser/meta.json +1 -1
  20. package/dist/lib/browser/meta.mjs +1 -1
  21. package/dist/lib/browser/types/index.mjs +2 -2
  22. package/dist/lib/node/AutomationPanel-UNLRYLMA.cjs +100 -0
  23. package/dist/lib/node/AutomationPanel-UNLRYLMA.cjs.map +7 -0
  24. package/dist/lib/node/ScriptContainer-BIBIIJHP.cjs +425 -0
  25. package/dist/lib/node/ScriptContainer-BIBIIJHP.cjs.map +7 -0
  26. package/dist/lib/node/{chunk-5RBVXKXT.cjs → chunk-25PQE7FL.cjs} +6 -6
  27. package/dist/lib/node/{chunk-5RBVXKXT.cjs.map → chunk-25PQE7FL.cjs.map} +2 -2
  28. package/dist/lib/node/{TypescriptEditor-M3WWFIP2.cjs → chunk-EJC55SDD.cjs} +18 -8
  29. package/dist/lib/node/chunk-EJC55SDD.cjs.map +7 -0
  30. package/dist/lib/node/{chunk-PFH3ROEH.cjs → chunk-FY5KKL5N.cjs} +22 -8
  31. package/dist/lib/node/chunk-FY5KKL5N.cjs.map +7 -0
  32. package/dist/lib/node/{chunk-4GTXIELQ.cjs → chunk-NKWNQMJH.cjs} +44 -6
  33. package/dist/lib/node/chunk-NKWNQMJH.cjs.map +7 -0
  34. package/dist/lib/node/chunk-NWBG5QDN.cjs +277 -0
  35. package/dist/lib/node/chunk-NWBG5QDN.cjs.map +7 -0
  36. package/dist/lib/node/edge/index.cjs +12 -8
  37. package/dist/lib/node/edge/index.cjs.map +2 -2
  38. package/dist/lib/node/index.cjs +103 -82
  39. package/dist/lib/node/index.cjs.map +4 -4
  40. package/dist/lib/node/meta.cjs +3 -3
  41. package/dist/lib/node/meta.cjs.map +1 -1
  42. package/dist/lib/node/meta.json +1 -1
  43. package/dist/lib/node/types/index.cjs +5 -5
  44. package/dist/lib/node/types/index.cjs.map +1 -1
  45. package/dist/lib/node-esm/AutomationPanel-D4NHEKM6.mjs +79 -0
  46. package/dist/lib/node-esm/AutomationPanel-D4NHEKM6.mjs.map +7 -0
  47. package/dist/lib/node-esm/ScriptContainer-4VQ46IAZ.mjs +424 -0
  48. package/dist/lib/node-esm/ScriptContainer-4VQ46IAZ.mjs.map +7 -0
  49. package/dist/lib/node-esm/chunk-7EDYDVQH.mjs +47 -0
  50. package/dist/lib/node-esm/chunk-7EDYDVQH.mjs.map +7 -0
  51. package/dist/lib/node-esm/chunk-EEDWDGZT.mjs +249 -0
  52. package/dist/lib/node-esm/chunk-EEDWDGZT.mjs.map +7 -0
  53. package/dist/lib/node-esm/chunk-GO7HYFM5.mjs +19 -0
  54. package/dist/lib/node-esm/chunk-GO7HYFM5.mjs.map +7 -0
  55. package/dist/lib/node-esm/chunk-QMTTZYLF.mjs +255 -0
  56. package/dist/lib/node-esm/chunk-QMTTZYLF.mjs.map +7 -0
  57. package/dist/lib/node-esm/chunk-YIISE4MX.mjs +280 -0
  58. package/dist/lib/node-esm/chunk-YIISE4MX.mjs.map +7 -0
  59. package/dist/lib/node-esm/edge/index.mjs +24 -0
  60. package/dist/lib/node-esm/index.mjs +317 -0
  61. package/dist/lib/node-esm/index.mjs.map +7 -0
  62. package/dist/lib/node-esm/meta.json +1 -0
  63. package/dist/lib/node-esm/meta.mjs +10 -0
  64. package/dist/lib/node-esm/meta.mjs.map +7 -0
  65. package/dist/lib/node-esm/types/index.mjs +13 -0
  66. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  67. package/dist/types/src/ScriptPlugin.d.ts.map +1 -1
  68. package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts +17 -0
  69. package/dist/types/src/components/AutomationPanel/AutomationPanel.d.ts.map +1 -0
  70. package/dist/types/src/components/AutomationPanel/index.d.ts +4 -0
  71. package/dist/types/src/components/AutomationPanel/index.d.ts.map +1 -0
  72. package/dist/types/src/components/{DebugPanel.d.ts → DebugPanel/DebugPanel.d.ts} +2 -1
  73. package/dist/types/src/components/DebugPanel/DebugPanel.d.ts.map +1 -0
  74. package/dist/types/src/components/DebugPanel/index.d.ts +2 -0
  75. package/dist/types/src/components/DebugPanel/index.d.ts.map +1 -0
  76. package/dist/types/src/components/{ScriptBlock/FrameContainer → FrameContainer}/FrameContainer.d.ts +1 -1
  77. package/dist/types/src/components/FrameContainer/FrameContainer.d.ts.map +1 -0
  78. package/dist/types/src/components/FrameContainer/frame.d.ts.map +1 -0
  79. package/dist/types/src/components/FrameContainer/index.d.ts.map +1 -0
  80. package/dist/types/src/components/ScriptBlock/ScriptBlock.d.ts +1 -3
  81. package/dist/types/src/components/ScriptBlock/ScriptBlock.d.ts.map +1 -1
  82. package/dist/types/src/components/ScriptBlock/ScriptBlock.stories.d.ts +3 -8
  83. package/dist/types/src/components/ScriptBlock/ScriptBlock.stories.d.ts.map +1 -1
  84. package/dist/types/src/components/ScriptContainer.d.ts +5 -0
  85. package/dist/types/src/components/ScriptContainer.d.ts.map +1 -0
  86. package/dist/types/src/components/ScriptEditor.d.ts +4 -5
  87. package/dist/types/src/components/ScriptEditor.d.ts.map +1 -1
  88. package/dist/types/src/components/ScriptSettings/ScriptSettings.d.ts +6 -0
  89. package/dist/types/src/components/ScriptSettings/ScriptSettings.d.ts.map +1 -0
  90. package/dist/types/src/components/ScriptSettings/index.d.ts +2 -0
  91. package/dist/types/src/components/ScriptSettings/index.d.ts.map +1 -0
  92. package/dist/types/src/components/Toolbar/Toolbar.d.ts +23 -0
  93. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  94. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +14 -0
  95. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  96. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  97. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  98. package/dist/types/src/components/{TypescriptEditor.d.ts → TypescriptEditor/TypescriptEditor.d.ts} +1 -2
  99. package/dist/types/src/components/TypescriptEditor/TypescriptEditor.d.ts.map +1 -0
  100. package/dist/types/src/components/TypescriptEditor/TypescriptEditor.stories.d.ts +6 -0
  101. package/dist/types/src/components/TypescriptEditor/TypescriptEditor.stories.d.ts.map +1 -0
  102. package/dist/types/src/components/TypescriptEditor/index.d.ts +2 -0
  103. package/dist/types/src/components/TypescriptEditor/index.d.ts.map +1 -0
  104. package/dist/types/src/components/index.d.ts +3 -3
  105. package/dist/types/src/components/index.d.ts.map +1 -1
  106. package/dist/types/src/edge/functions.d.ts +7 -0
  107. package/dist/types/src/edge/functions.d.ts.map +1 -1
  108. package/dist/types/src/meta.d.ts +4 -9
  109. package/dist/types/src/meta.d.ts.map +1 -1
  110. package/dist/types/src/templates/chat.d.ts +6 -0
  111. package/dist/types/src/templates/chat.d.ts.map +1 -0
  112. package/dist/types/src/templates/chess.d.ts +6 -0
  113. package/dist/types/src/templates/chess.d.ts.map +1 -0
  114. package/dist/types/src/templates/echo.d.ts +6 -0
  115. package/dist/types/src/templates/echo.d.ts.map +1 -0
  116. package/dist/types/src/templates/email.d.ts +6 -0
  117. package/dist/types/src/templates/email.d.ts.map +1 -0
  118. package/dist/types/src/templates/forex.d.ts +6 -0
  119. package/dist/types/src/templates/forex.d.ts.map +1 -0
  120. package/dist/types/src/templates.d.ts +8 -0
  121. package/dist/types/src/templates.d.ts.map +1 -0
  122. package/dist/types/src/translations.d.ts +2 -0
  123. package/dist/types/src/translations.d.ts.map +1 -1
  124. package/dist/types/src/types/script.d.ts +4 -4
  125. package/dist/types/src/types/types.d.ts +4 -3
  126. package/dist/types/src/types/types.d.ts.map +1 -1
  127. package/package.json +62 -55
  128. package/src/ScriptPlugin.tsx +26 -39
  129. package/src/bundler/bundler.test.ts +12 -2
  130. package/src/components/AutomationPanel/AutomationPanel.tsx +101 -0
  131. package/src/components/AutomationPanel/index.ts +9 -0
  132. package/src/components/{DebugPanel.tsx → DebugPanel/DebugPanel.tsx} +83 -78
  133. package/src/components/DebugPanel/index.ts +5 -0
  134. package/src/components/{ScriptBlock/FrameContainer → FrameContainer}/FrameContainer.tsx +1 -1
  135. package/src/components/ScriptBlock/ScriptBlock.stories.tsx +15 -14
  136. package/src/components/ScriptBlock/ScriptBlock.tsx +23 -51
  137. package/src/components/ScriptContainer.tsx +22 -0
  138. package/src/components/ScriptEditor.tsx +58 -55
  139. package/src/components/ScriptSettings/ScriptSettings.tsx +47 -0
  140. package/src/components/ScriptSettings/index.ts +5 -0
  141. package/src/components/Toolbar/Toolbar.stories.tsx +30 -0
  142. package/src/components/Toolbar/Toolbar.tsx +177 -0
  143. package/src/components/{ScriptBlock/Splitter → Toolbar}/index.ts +1 -1
  144. package/src/components/{TypescriptEditor.stories.tsx → TypescriptEditor/TypescriptEditor.stories.tsx} +17 -35
  145. package/src/components/{TypescriptEditor.tsx → TypescriptEditor/TypescriptEditor.tsx} +1 -3
  146. package/src/components/TypescriptEditor/index.ts +5 -0
  147. package/src/components/index.ts +5 -3
  148. package/src/edge/functions.ts +19 -0
  149. package/src/meta.ts +15 -0
  150. package/src/templates/chat.ts +102 -0
  151. package/src/templates/chess.ts +41 -0
  152. package/src/templates/echo.ts +15 -0
  153. package/src/templates/email.ts +38 -0
  154. package/src/templates/forex.ts +21 -0
  155. package/src/templates.ts +54 -0
  156. package/src/translations.ts +2 -0
  157. package/src/types/types.ts +9 -5
  158. package/dist/lib/browser/ScriptEditor-5Y3EROG3.mjs +0 -513
  159. package/dist/lib/browser/ScriptEditor-5Y3EROG3.mjs.map +0 -7
  160. package/dist/lib/browser/TypescriptEditor-GPE72Y6I.mjs +0 -9
  161. package/dist/lib/browser/chunk-DL5XQRQM.mjs.map +0 -7
  162. package/dist/lib/browser/chunk-JBACGDOJ.mjs +0 -21
  163. package/dist/lib/browser/chunk-JBACGDOJ.mjs.map +0 -7
  164. package/dist/lib/browser/chunk-MRGOT5RF.mjs +0 -78
  165. package/dist/lib/browser/chunk-MRGOT5RF.mjs.map +0 -7
  166. package/dist/lib/node/ScriptEditor-YCUQFWQM.cjs +0 -526
  167. package/dist/lib/node/ScriptEditor-YCUQFWQM.cjs.map +0 -7
  168. package/dist/lib/node/TypescriptEditor-M3WWFIP2.cjs.map +0 -7
  169. package/dist/lib/node/chunk-4GTXIELQ.cjs.map +0 -7
  170. package/dist/lib/node/chunk-4VMNBXWU.cjs +0 -54
  171. package/dist/lib/node/chunk-4VMNBXWU.cjs.map +0 -7
  172. package/dist/lib/node/chunk-IMLDKD5Y.cjs +0 -111
  173. package/dist/lib/node/chunk-IMLDKD5Y.cjs.map +0 -7
  174. package/dist/lib/node/chunk-PFH3ROEH.cjs.map +0 -7
  175. package/dist/types/src/components/DebugPanel.d.ts.map +0 -1
  176. package/dist/types/src/components/ScriptBlock/FrameContainer/FrameContainer.d.ts.map +0 -1
  177. package/dist/types/src/components/ScriptBlock/FrameContainer/frame.d.ts.map +0 -1
  178. package/dist/types/src/components/ScriptBlock/FrameContainer/index.d.ts.map +0 -1
  179. package/dist/types/src/components/ScriptBlock/Splitter/Splitter.d.ts +0 -15
  180. package/dist/types/src/components/ScriptBlock/Splitter/Splitter.d.ts.map +0 -1
  181. package/dist/types/src/components/ScriptBlock/Splitter/Splitter.stories.d.ts +0 -13
  182. package/dist/types/src/components/ScriptBlock/Splitter/Splitter.stories.d.ts.map +0 -1
  183. package/dist/types/src/components/ScriptBlock/Splitter/index.d.ts +0 -2
  184. package/dist/types/src/components/ScriptBlock/Splitter/index.d.ts.map +0 -1
  185. package/dist/types/src/components/Toolbar.d.ts +0 -13
  186. package/dist/types/src/components/Toolbar.d.ts.map +0 -1
  187. package/dist/types/src/components/Toolbar.stories.d.ts +0 -14
  188. package/dist/types/src/components/Toolbar.stories.d.ts.map +0 -1
  189. package/dist/types/src/components/TypescriptEditor.d.ts.map +0 -1
  190. package/dist/types/src/components/TypescriptEditor.stories.d.ts +0 -10
  191. package/dist/types/src/components/TypescriptEditor.stories.d.ts.map +0 -1
  192. package/src/components/ScriptBlock/Splitter/Splitter.stories.tsx +0 -37
  193. package/src/components/ScriptBlock/Splitter/Splitter.tsx +0 -52
  194. package/src/components/Toolbar.stories.tsx +0 -17
  195. package/src/components/Toolbar.tsx +0 -147
  196. package/src/meta.tsx +0 -19
  197. /package/dist/lib/{browser/TypescriptEditor-GPE72Y6I.mjs.map → node-esm/edge/index.mjs.map} +0 -0
  198. /package/dist/types/src/components/{ScriptBlock/FrameContainer → FrameContainer}/frame.d.ts +0 -0
  199. /package/dist/types/src/components/{ScriptBlock/FrameContainer → FrameContainer}/index.d.ts +0 -0
  200. /package/src/components/{ScriptBlock/FrameContainer → FrameContainer}/frame.html +0 -0
  201. /package/src/components/{ScriptBlock/FrameContainer → FrameContainer}/frame.tsx +0 -0
  202. /package/src/components/{ScriptBlock/FrameContainer → FrameContainer}/index.ts +0 -0
@@ -5,20 +5,11 @@
5
5
  import React, { useEffect, useRef, useState } from 'react';
6
6
 
7
7
  import { log } from '@dxos/log';
8
- import {
9
- Avatar,
10
- DensityProvider,
11
- Icon,
12
- Input,
13
- type ThemedClassName,
14
- Toolbar,
15
- useControlledValue,
16
- useTranslation,
17
- } from '@dxos/react-ui';
8
+ import { Avatar, Icon, Input, type ThemedClassName, Toolbar, useControlledValue, useTranslation } from '@dxos/react-ui';
18
9
  import { SyntaxHighlighter } from '@dxos/react-ui-syntax-highlighter';
19
10
  import { mx } from '@dxos/react-ui-theme';
20
11
 
21
- import { SCRIPT_PLUGIN } from '../meta';
12
+ import { SCRIPT_PLUGIN } from '../../meta';
22
13
 
23
14
  type State = 'pending' | 'responding';
24
15
 
@@ -31,9 +22,16 @@ export type DebugPanelProps = ThemedClassName<{
31
22
  functionUrl?: string;
32
23
  binding?: string;
33
24
  onBindingChange?: (binding: string) => void;
25
+ showBindingConfig?: boolean;
34
26
  }>;
35
27
 
36
- export const DebugPanel = ({ classNames, functionUrl, binding: _binding, onBindingChange }: DebugPanelProps) => {
28
+ export const DebugPanel = ({
29
+ classNames,
30
+ functionUrl,
31
+ binding: _binding,
32
+ onBindingChange,
33
+ showBindingConfig = false,
34
+ }: DebugPanelProps) => {
37
35
  const { t } = useTranslation(SCRIPT_PLUGIN);
38
36
 
39
37
  const bindingInputRef = useRef<HTMLInputElement>(null);
@@ -47,7 +45,7 @@ export const DebugPanel = ({ classNames, functionUrl, binding: _binding, onBindi
47
45
  const [result, setResult] = useState('');
48
46
  const [state, setState] = useState<State | null>(null);
49
47
 
50
- // TODO(burdon): Persistent history (non-space ECHO data?)
48
+ // TODO(burdon): Persistent history -- at least for session (non-space ECHO data?)
51
49
  const [history, setHistory] = useState<Message[]>([]);
52
50
 
53
51
  const scrollerRef = useRef<HTMLDivElement>(null);
@@ -175,24 +173,24 @@ export const DebugPanel = ({ classNames, functionUrl, binding: _binding, onBindi
175
173
  };
176
174
 
177
175
  return (
178
- <DensityProvider density='fine'>
179
- <div className={mx('flex flex-col h-full overflow-hidden', classNames)}>
180
- {/* TODO(burdon): Replace with Thread? */}
181
- <div ref={scrollerRef} className='flex flex-col gap-6 h-full p-4 overflow-x-hidden overflow-y-auto'>
182
- <MessageThread state={state} result={result} history={history} />
183
- </div>
176
+ <div className={mx('flex flex-col h-full overflow-hidden', classNames)}>
177
+ {/* TODO(burdon): Replace with Thread? */}
178
+ <div ref={scrollerRef} className='flex flex-col gap-6 h-full p-2 overflow-x-hidden overflow-y-auto'>
179
+ <MessageThread state={state} result={result} history={history} />
180
+ </div>
184
181
 
185
- <Toolbar.Root classNames='p-2'>
186
- <Input.Root>
187
- <Input.TextInput
188
- ref={inputRef}
189
- autoFocus
190
- placeholder={t('function request placeholder')}
191
- value={input}
192
- onChange={(ev) => setInput(ev.target.value)}
193
- onKeyDown={(ev) => ev.key === 'Enter' && handleRequest(input)}
194
- />
195
- </Input.Root>
182
+ <Toolbar.Root classNames='p-2'>
183
+ <Input.Root>
184
+ <Input.TextInput
185
+ ref={inputRef}
186
+ autoFocus
187
+ placeholder={t('function request placeholder')}
188
+ value={input}
189
+ onChange={(ev) => setInput(ev.target.value)}
190
+ onKeyDown={(ev) => ev.key === 'Enter' && handleRequest(input)}
191
+ />
192
+ </Input.Root>
193
+ {showBindingConfig && (
196
194
  <Input.Root>
197
195
  <Input.TextInput
198
196
  ref={bindingInputRef}
@@ -203,67 +201,74 @@ export const DebugPanel = ({ classNames, functionUrl, binding: _binding, onBindi
203
201
  onBlur={handleBlur}
204
202
  />
205
203
  </Input.Root>
206
- <Toolbar.Button onClick={() => handleRequest(input)}>
207
- <Icon icon='ph--play--regular' size={4} />
208
- </Toolbar.Button>
209
- <Toolbar.Button onClick={() => (state ? handleStop() : handleClear())}>
210
- <Icon icon={state ? 'ph--stop--regular' : 'ph--trash--regular'} size={4} />
211
- </Toolbar.Button>
212
- </Toolbar.Root>
213
- </div>
214
- </DensityProvider>
204
+ )}
205
+ <Toolbar.Button onClick={() => handleRequest(input)}>
206
+ <Icon icon='ph--play--regular' size={4} />
207
+ </Toolbar.Button>
208
+ <Toolbar.Button onClick={() => (state ? handleStop() : handleClear())}>
209
+ <Icon icon={state ? 'ph--stop--regular' : 'ph--trash--regular'} size={4} />
210
+ </Toolbar.Button>
211
+ </Toolbar.Root>
212
+ </div>
215
213
  );
216
214
  };
217
215
 
218
- const RobotAvatar = () => (
219
- <Avatar.Root size={6} variant='circle'>
220
- <Avatar.Frame>
221
- <Avatar.Icon icon='ph--robot--regular' />
222
- </Avatar.Frame>
223
- </Avatar.Root>
224
- );
225
-
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
216
  // TODO(burdon): Replace with thread?
242
217
  // TODO(burdon): Button to delete individual messages.
243
- // TODO(burdon): Button to re-run question.
218
+ // TODO(burdon): Button to edit/re-run question.
244
219
  const MessageThread = ({ state, result, history }: { state: State | null; result: string; history: Message[] }) => {
245
220
  return (
246
221
  <>
247
222
  {history.map((message, i) => (
248
- <div key={i} className='grid grid-cols-[3rem_1fr]'>
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
- />
223
+ <div key={i} className='grid grid-cols-[2rem_1fr_2rem]'>
224
+ <div className='p-1'>{message.type === 'response' && <RobotAvatar />}</div>
225
+ <div className='overflow-auto'>
226
+ <MessageItem message={message} />
255
227
  </div>
256
228
  </div>
257
229
  ))}
258
- <div className='grid grid-cols-[3rem_1fr]'>
259
- <div>
260
- {(state === 'pending' && <Icon icon='ph--spinner--regular' size={6} classNames='animate-spin-slow' />) ||
261
- (result.length > 0 && <RobotAvatar />)}
262
- </div>
263
- <div>
264
- <span>{result}</span>
230
+ {result?.length > 0 && (
231
+ <div className='grid grid-cols-[2rem_1fr_2rem]'>
232
+ <div className='p-1'>
233
+ {(state === 'pending' && <Icon icon='ph--spinner--regular' size={6} classNames='animate-spin' />) || (
234
+ <Icon icon='ph--robot--regular' size={6} classNames='animate-[pulse_1s_ease-in-out_infinite]' />
235
+ )}
236
+ </div>
237
+ <div className='overflow-auto'>
238
+ <MessageItem message={{ type: 'response', text: result }} />
239
+ </div>
265
240
  </div>
266
- </div>
241
+ )}
267
242
  </>
268
243
  );
269
244
  };
245
+
246
+ const MessageItem = ({ classNames, message }: ThemedClassName<{ message: Message }>) => {
247
+ const { type, text, data, error } = message;
248
+ const wrapper = 'p-1 px-2 rounded-lg bg-hoverSurface overflow-auto';
249
+ return (
250
+ <div className={mx('flex', type === 'request' ? 'ml-[1rem] justify-end' : 'mr-[1rem]', classNames)}>
251
+ {error && <div className={mx(wrapper, 'whitespace-pre text-error')}>{String(error)}</div>}
252
+
253
+ {text !== undefined && (
254
+ <div className={mx(wrapper, type === 'request' && 'bg-primary-400 dark:bg-primary-600')}>
255
+ {text || '\u00D8'}
256
+ </div>
257
+ )}
258
+
259
+ {data && (
260
+ <SyntaxHighlighter language='json' className={mx(wrapper, 'px-8 py-2 text-xs')}>
261
+ {JSON.stringify(data, null, 2)}
262
+ </SyntaxHighlighter>
263
+ )}
264
+ </div>
265
+ );
266
+ };
267
+
268
+ const RobotAvatar = () => (
269
+ <Avatar.Root size={6} variant='circle'>
270
+ <Avatar.Frame>
271
+ <Avatar.Icon icon='ph--robot--regular' />
272
+ </Avatar.Frame>
273
+ </Avatar.Root>
274
+ );
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2024 DXOS.org
3
+ //
4
+
5
+ export * from './DebugPanel';
@@ -11,7 +11,7 @@ import { baseSurface, mx } from '@dxos/react-ui-theme';
11
11
  import { createProtoRpcPeer } from '@dxos/rpc';
12
12
  import { createIFramePort } from '@dxos/rpc-tunnel';
13
13
 
14
- import { type BundlerResult } from '../../../bundler';
14
+ import { type BundlerResult } from '../../bundler';
15
15
 
16
16
  export type FrameContainerProps = {
17
17
  containerUrl: string;
@@ -3,19 +3,21 @@
3
3
  //
4
4
 
5
5
  import '@dxos-theme';
6
+
7
+ import { type Meta } from '@storybook/react';
6
8
  import React, { useEffect, useMemo } from 'react';
7
9
 
8
10
  import { createSpaceObjectGenerator, TestSchemaType } from '@dxos/echo-generator';
9
11
  import { TextType } from '@dxos/plugin-markdown/types';
10
12
  import { useClient } from '@dxos/react-client';
11
- import { create, createEchoObject, useSpaces } from '@dxos/react-client/echo';
13
+ import { create, createObject, useSpaces } from '@dxos/react-client/echo';
12
14
  import { withClientProvider } from '@dxos/react-client/testing';
13
15
  import { withLayout, withTheme } from '@dxos/storybook-utils';
14
16
 
15
- // @ts-ignore
16
- import mainUrl from './FrameContainer/frame?url';
17
17
  import { ScriptBlock } from './ScriptBlock';
18
18
  import { ScriptType } from '../../types';
19
+ // @ts-ignore
20
+ import mainUrl from '../FrameContainer/frame?url';
19
21
 
20
22
  const code = [
21
23
  "import { Filter, useQuery, useSpaces} from '@dxos/react-client/echo';",
@@ -29,7 +31,7 @@ const code = [
29
31
  '}',
30
32
  ].join('\n');
31
33
 
32
- const Story = () => {
34
+ const DefaultStory = () => {
33
35
  const client = useClient();
34
36
  const spaces = useSpaces();
35
37
  console.log(spaces.length);
@@ -44,28 +46,27 @@ const Story = () => {
44
46
  [TestSchemaType.organization]: 20,
45
47
  [TestSchemaType.contact]: 50,
46
48
  })
47
- .catch();
49
+ .catch(() => {});
48
50
  } catch (err) {
49
51
  console.log(err);
50
52
  }
51
53
  }, [client]);
52
54
 
53
55
  // TODO(burdon): Review what's the right way to create automerge-backed objects.
54
- const object = useMemo(
55
- () => createEchoObject(create(ScriptType, { source: create(TextType, { content: code }) })),
56
- [],
57
- );
56
+ const object = useMemo(() => createObject(create(ScriptType, { source: create(TextType, { content: code }) })), []);
58
57
 
59
58
  // TODO(dmaretskyi): Not sure how to provide `containerUrl` here since the html now lives in composer-app.
60
59
  // TODO(burdon): Normalize html/frame.tsx with composer-app to test locally.
61
60
  return <ScriptBlock script={object} containerUrl={mainUrl} />;
62
61
  };
63
62
 
64
- export default {
65
- title: 'plugin-script/ScriptBlock',
63
+ export const Default = {};
64
+
65
+ const meta: Meta = {
66
+ title: 'plugins/plugin-script/ScriptBlock',
66
67
  component: ScriptBlock,
67
- decorators: [withTheme, withLayout({ fullscreen: true, tooltips: true }), withClientProvider({ createSpace: true })],
68
- render: Story,
68
+ render: DefaultStory,
69
+ decorators: [withClientProvider({ createSpace: true }), withTheme, withLayout({ fullscreen: true, tooltips: true })],
69
70
  };
70
71
 
71
- export const Default = {};
72
+ export default meta;
@@ -2,19 +2,17 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { Check, Play, Warning } from '@phosphor-icons/react';
6
5
  // @ts-ignore
7
6
  import wasmUrl from 'esbuild-wasm/esbuild.wasm?url';
8
7
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
9
8
 
10
9
  import { createDocAccessor, DocAccessor } from '@dxos/react-client/echo';
11
- import { DensityProvider, Toolbar, Button } from '@dxos/react-ui';
12
- import { mx, getSize } from '@dxos/react-ui-theme';
10
+ import { Toolbar, Button, Icon } from '@dxos/react-ui';
11
+ import { mx } from '@dxos/react-ui-theme';
13
12
 
14
- import { FrameContainer } from './FrameContainer';
15
- import { Splitter, SplitterSelector, type View } from './Splitter';
16
13
  import { Bundler, type BundlerResult, initializeBundler } from '../../bundler';
17
14
  import { type ScriptType } from '../../types';
15
+ import { FrameContainer } from '../FrameContainer';
18
16
  import { ScriptEditor } from '../ScriptEditor';
19
17
 
20
18
  // Keep in sync with packages/apps/composer-app/script-frame/main.tsx .
@@ -29,7 +27,6 @@ const PROVIDED_MODULES = [
29
27
 
30
28
  export type ScriptBlockProps = {
31
29
  script: ScriptType;
32
- view?: View;
33
30
  hideSelector?: boolean;
34
31
  classes?: {
35
32
  root?: string;
@@ -44,17 +41,8 @@ export type ScriptBlockProps = {
44
41
  * @deprecated
45
42
  */
46
43
  // TODO(burdon): Cache compiled results in context.
47
- export const ScriptBlock = ({
48
- script,
49
- view: controlledView,
50
- hideSelector,
51
- classes,
52
- containerUrl,
53
- }: ScriptBlockProps) => {
44
+ export const ScriptBlock = ({ script, hideSelector, classes, containerUrl }: ScriptBlockProps) => {
54
45
  const source = useMemo(() => script.source && createDocAccessor(script.source, ['content']), [script.source]);
55
- const [view, setView] = useState<View>(controlledView ?? 'editor');
56
- useEffect(() => handleSetView(controlledView ?? 'editor'), [controlledView]);
57
-
58
46
  const [result, setResult] = useState<BundlerResult>();
59
47
  const bundler = useMemo(
60
48
  () => new Bundler({ platform: 'browser', sandboxedModules: PROVIDED_MODULES, remoteModules: {} }),
@@ -78,16 +66,6 @@ export const ScriptBlock = ({
78
66
  return () => clearTimeout(t);
79
67
  }, [source]);
80
68
 
81
- const handleSetView = useCallback(
82
- (view: View) => {
83
- setView(view);
84
- if (!result && view !== 'editor') {
85
- void handleExec(false);
86
- }
87
- },
88
- [result],
89
- );
90
-
91
69
  const handleExec = useCallback(
92
70
  async (auto = true) => {
93
71
  if (!source) {
@@ -95,41 +73,35 @@ export const ScriptBlock = ({
95
73
  }
96
74
  const result = await bundler.bundle(DocAccessor.getValue(source));
97
75
  setResult(result);
98
- if (auto && view === 'editor') {
99
- setView('preview');
100
- }
101
76
  },
102
- [source, view],
77
+ [source],
103
78
  );
104
79
 
105
80
  return (
106
81
  <div className={mx('flex flex-col grow overflow-hidden', classes?.root)}>
107
82
  {!hideSelector && (
108
- <DensityProvider density='fine'>
109
- <Toolbar.Root classNames={mx('mb-2', classes?.toolbar)}>
110
- <SplitterSelector view={view} onChange={handleSetView} />
111
- <div className='grow' />
112
- {result?.bundle && !result?.error && (
113
- <div title={String(result.error)}>
114
- <Check className={mx(getSize(5), 'text-green-500')} />
115
- </div>
116
- )}
117
- {result?.error && (
118
- <div title={String(result.error)}>
119
- <Warning className={mx(getSize(5), 'text-orange-500')} />
120
- </div>
121
- )}
122
- <Button variant='ghost' onClick={() => handleExec()}>
123
- <Play className={getSize(5)} />
124
- </Button>
125
- </Toolbar.Root>
126
- </DensityProvider>
83
+ <Toolbar.Root classNames={mx('mb-2', classes?.toolbar)}>
84
+ <div className='grow' />
85
+ {result?.bundle && !result?.error && (
86
+ <div title={String(result.error)}>
87
+ <Icon icon='ph--check--regular' size={5} classNames='text-green-500' />
88
+ </div>
89
+ )}
90
+ {result?.error && (
91
+ <div title={String(result.error)}>
92
+ <Icon icon='ph--warning--regular' size={5} classNames='text-orange-500' />
93
+ </div>
94
+ )}
95
+ <Button variant='ghost' onClick={() => handleExec()}>
96
+ <Icon icon='ph--play--regular' size={5} />
97
+ </Button>
98
+ </Toolbar.Root>
127
99
  )}
128
100
 
129
- <Splitter view={view}>
101
+ <div className='flex'>
130
102
  <ScriptEditor script={script} />
131
103
  {result && <FrameContainer key={script.id} result={result} containerUrl={containerUrl} />}
132
- </Splitter>
104
+ </div>
133
105
  </div>
134
106
  );
135
107
  };
@@ -0,0 +1,22 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import React from 'react';
6
+
7
+ import { fullyQualifiedId } from '@dxos/react-client/echo';
8
+ import { useAttendableAttributes } from '@dxos/react-ui-attention';
9
+ import { mx } from '@dxos/react-ui-theme';
10
+
11
+ import { ScriptEditor, type ScriptEditorProps } from './ScriptEditor';
12
+
13
+ const ScriptContainer = ({ script, ...props }: ScriptEditorProps) => {
14
+ const attendableAttrs = useAttendableAttributes(fullyQualifiedId(script));
15
+ return (
16
+ <div role='none' className={mx('flex row-span-2 w-full overflow-hidden attention-surface')} {...attendableAttrs}>
17
+ <ScriptEditor {...props} script={script} />
18
+ </div>
19
+ );
20
+ };
21
+
22
+ export default ScriptContainer;
@@ -10,45 +10,39 @@ 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 {
14
- create,
15
- createDocAccessor,
16
- Filter,
17
- fullyQualifiedId,
18
- getMeta,
19
- getSpace,
20
- useQuery,
21
- } from '@dxos/react-client/echo';
13
+ import { create, createDocAccessor, Filter, getMeta, getSpace, useQuery } from '@dxos/react-client/echo';
22
14
  import { useIdentity } from '@dxos/react-client/halo';
23
- import { useTranslation } from '@dxos/react-ui';
24
- import { useHasAttention } from '@dxos/react-ui-attention';
15
+ import { useTranslation, type ThemedClassName } from '@dxos/react-ui';
25
16
  import { createDataExtensions, listener } from '@dxos/react-ui-editor';
26
17
  import { mx } from '@dxos/react-ui-theme';
27
18
 
28
19
  import { DebugPanel } from './DebugPanel';
29
- import { Toolbar } from './Toolbar';
20
+ import { type TemplateSelectProps, Toolbar, type ViewType } from './Toolbar';
30
21
  import { TypescriptEditor, type TypescriptEditorProps } from './TypescriptEditor';
31
22
  import { Bundler } from '../bundler';
32
23
  import {
24
+ getInvocationUrl,
33
25
  getUserFunctionUrlInMetadata,
34
26
  publicKeyToDid,
35
27
  setUserFunctionUrlInMetadata,
36
28
  uploadWorkerFunction,
29
+ USERFUNCTIONS_PRESET_META_KEY,
37
30
  } from '../edge';
38
31
  import { SCRIPT_PLUGIN } from '../meta';
32
+ import { templates } from '../templates';
39
33
  import { FunctionType, type ScriptType } from '../types';
40
34
 
41
- export type ScriptEditorProps = {
35
+ export type ScriptEditorProps = ThemedClassName<{
42
36
  script: ScriptType;
43
- role?: string;
44
- } & Pick<TypescriptEditorProps, 'env'>;
37
+ }> &
38
+ Pick<TypescriptEditorProps, 'env'>;
45
39
 
46
- export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
40
+ export const ScriptEditor = ({ classNames, script, env }: ScriptEditorProps) => {
47
41
  const { t } = useTranslation(SCRIPT_PLUGIN);
48
- const hasAttention = useHasAttention(fullyQualifiedId(script));
49
42
  const client = useClient();
50
43
  const identity = useIdentity();
51
44
  const space = getSpace(script);
45
+ // TODO(dmaretskyi): Parametric query.
52
46
  const [fn] = useQuery(
53
47
  space,
54
48
  Filter.schema(FunctionType, (fn) => fn.source === script),
@@ -73,7 +67,7 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
73
67
  [script, script.source, space, identity],
74
68
  );
75
69
 
76
- const [showPanel, setShowPanel] = useState(false);
70
+ const [view, setView] = useState<ViewType>('editor');
77
71
  const initialValue = useMemo(() => script.source?.content, [script.source]);
78
72
  const existingFunctionUrl = fn && getUserFunctionUrlInMetadata(getMeta(fn));
79
73
  const [error, setError] = useState<string>();
@@ -103,6 +97,22 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
103
97
  }
104
98
  }, [script.source]);
105
99
 
100
+ const handleTemplateChange: TemplateSelectProps['onTemplateSelect'] = (id) => {
101
+ const template = templates.find((template) => template.id === id);
102
+ if (template) {
103
+ script.name = template.name;
104
+ script.source!.content = template.source;
105
+ const metaKeys = getMeta(script).keys;
106
+ const oldPresetIndex = metaKeys.findIndex((key) => key.source === USERFUNCTIONS_PRESET_META_KEY);
107
+ if (oldPresetIndex >= 0) {
108
+ metaKeys.splice(oldPresetIndex, 1);
109
+ }
110
+ if (template.presetId) {
111
+ metaKeys.push({ source: USERFUNCTIONS_PRESET_META_KEY, id: template.presetId });
112
+ }
113
+ }
114
+ };
115
+
106
116
  const handleDeploy = useCallback(async () => {
107
117
  if (!script.source || !identity || !space) {
108
118
  return;
@@ -114,11 +124,7 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
114
124
  const existingFunctionId = existingFunctionUrl?.split('/').at(-1);
115
125
  const ownerDid = (existingFunctionUrl?.split('/').at(-2) as DID) ?? publicKeyToDid(identity.identityKey);
116
126
 
117
- const bundler = new Bundler({
118
- platform: 'browser',
119
- sandboxedModules: [],
120
- remoteModules: {},
121
- });
127
+ const bundler = new Bundler({ platform: 'browser', sandboxedModules: [], remoteModules: {} });
122
128
  const buildResult = await bundler.bundle(script.source.content);
123
129
  if (buildResult.error || !buildResult.bundle) {
124
130
  throw buildResult.error;
@@ -134,15 +140,18 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
134
140
  if (result !== 'success' || functionId === undefined || functionVersionNumber === undefined) {
135
141
  throw new Error(errorMessage);
136
142
  }
143
+
137
144
  log.info('function uploaded', { functionId, functionVersionNumber });
138
145
  if (fn) {
139
146
  fn.version = functionVersionNumber;
140
147
  }
148
+
141
149
  const deployedFunction =
142
150
  fn ?? space.db.add(create(FunctionType, { version: functionVersionNumber, source: script }));
143
151
  const meta = getMeta(deployedFunction);
144
- setUserFunctionUrlInMetadata(meta, `/${ownerDid}/${functionId}`);
145
152
  script.changed = false;
153
+ setUserFunctionUrlInMetadata(meta, `/${ownerDid}/${functionId}`);
154
+ setView('split');
146
155
  } catch (err: any) {
147
156
  log.catch(err);
148
157
  setError(t('upload failed label'));
@@ -154,49 +163,43 @@ export const ScriptEditor = ({ env, script, role }: ScriptEditorProps) => {
154
163
  return;
155
164
  }
156
165
 
157
- const baseUrl = new URL('functions/', client.config.values.runtime?.services?.edge?.url);
158
-
159
- // Leading slashes cause the URL to be treated as an absolute path.
160
- const relativeUrl = existingFunctionUrl.replace(/^\//, '');
161
- const url = new URL(`./${relativeUrl}`, baseUrl.toString());
162
- space && url.searchParams.set('spaceId', space.id);
163
- url.protocol = 'https';
164
- return url.toString();
166
+ return getInvocationUrl(existingFunctionUrl, client.config.values.runtime?.services?.edge?.url ?? '', {
167
+ spaceId: space?.id,
168
+ });
165
169
  }, [existingFunctionUrl, space]);
166
170
 
167
171
  return (
168
- <div
169
- role='none'
170
- className={mx(
171
- 'flex flex-col is-full bs-full overflow-hidden',
172
- role === 'article' && 'row-span-2',
173
- hasAttention && 'attention-surface',
174
- )}
175
- >
176
- <TypescriptEditor
177
- id={script.id}
178
- env={env}
179
- initialValue={initialValue}
180
- extensions={extensions}
181
- className='flex is-full bs-full overflow-hidden ch-focus-ring-inset-over-all'
182
- />
183
-
172
+ <div role='none' className={mx('flex flex-col w-full overflow-hidden divide-y divide-separator', classNames)}>
184
173
  <Toolbar
185
- classNames='border-t border-separator'
186
174
  deployed={Boolean(existingFunctionUrl) && !script.changed}
187
175
  functionUrl={functionUrl}
188
176
  error={error}
189
- showPanel={showPanel}
177
+ view={view}
178
+ templates={templates}
190
179
  onDeploy={handleDeploy}
191
180
  onFormat={handleFormat}
192
- onTogglePanel={async () => setShowPanel((showDetails) => !showDetails)}
181
+ onViewChange={setView}
182
+ onTemplateSelect={handleTemplateChange}
193
183
  />
194
184
 
195
- {showPanel && (
196
- <DebugPanel functionUrl={functionUrl} binding={fn?.binding} onBindingChange={handleBindingChange} />
185
+ {view !== 'preview' && (
186
+ <TypescriptEditor
187
+ id={script.id}
188
+ env={env}
189
+ initialValue={initialValue}
190
+ extensions={extensions}
191
+ className='flex is-full bs-full overflow-hidden ch-focus-ring-inset-over-all'
192
+ />
193
+ )}
194
+
195
+ {view !== 'editor' && (
196
+ <DebugPanel
197
+ functionUrl={functionUrl}
198
+ showBindingConfig
199
+ binding={fn?.binding}
200
+ onBindingChange={handleBindingChange}
201
+ />
197
202
  )}
198
203
  </div>
199
204
  );
200
205
  };
201
-
202
- export default ScriptEditor;