@flowdular/sdk 0.1.0 → 0.2.1

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 (175) hide show
  1. package/.ai/skills/test-hardening/SKILL.md +1 -1
  2. package/README.md +69 -12
  3. package/assets/flowdular-banner.png +0 -0
  4. package/modules/agents/package.json +1 -1
  5. package/modules/auth/package.json +1 -1
  6. package/modules/auth/src/client/AuthenticationCore.tsrx +3 -1
  7. package/modules/auth/src/server/endpoints.ts +3 -1
  8. package/modules/auth/src/server/runtime.ts +4 -0
  9. package/modules/automations/package.json +1 -1
  10. package/modules/automations-workflows-integration/package.json +1 -1
  11. package/modules/profile/package.json +1 -1
  12. package/modules/sandbox/package.json +1 -1
  13. package/modules/system/package.json +1 -1
  14. package/modules/users/package.json +1 -1
  15. package/modules/workflows/package.json +1 -1
  16. package/package.json +5 -13
  17. package/packages/ai-provider/package.json +1 -1
  18. package/packages/cli-protocol/package.json +1 -1
  19. package/packages/client/package.json +1 -1
  20. package/packages/client/src/routing.ts +39 -2
  21. package/packages/client/src/shell/BrandMark.tsrx +5 -1
  22. package/packages/client/src/shell/navigation.ts +3 -1
  23. package/packages/client/src/state.ts +6 -2
  24. package/packages/contracts/package.json +1 -1
  25. package/packages/contracts/schemas/project.schema.json +16 -42
  26. package/packages/database/package.json +1 -1
  27. package/packages/database-pglite/package.json +2 -2
  28. package/packages/database-pglite/src/driver.ts +21 -3
  29. package/packages/database-testing/package.json +1 -1
  30. package/packages/dev-console/package.json +1 -1
  31. package/packages/harness/package.json +1 -1
  32. package/packages/kernel/package.json +1 -1
  33. package/packages/server/package.json +1 -1
  34. package/packages/server/src/application-routes.ts +80 -8
  35. package/packages/server/src/index.ts +4 -1
  36. package/packages/server/src/web-html.ts +9 -5
  37. package/packages/server/src/web.ts +59 -7
  38. package/packages/ui/package.json +1 -1
  39. package/packages/coding-agent/package.json +0 -44
  40. package/packages/coding-agent/src/drivers/byok.ts +0 -322
  41. package/packages/coding-agent/src/drivers/claude-code.ts +0 -290
  42. package/packages/coding-agent/src/drivers/codex.ts +0 -281
  43. package/packages/coding-agent/src/index.ts +0 -56
  44. package/packages/coding-agent/src/registry.ts +0 -119
  45. package/packages/coding-agent/src/roles/contract.ts +0 -60
  46. package/packages/coding-agent/src/roles/defaults.ts +0 -134
  47. package/packages/coding-agent/src/roles/handoff.ts +0 -41
  48. package/packages/coding-agent/src/roles/registry.ts +0 -156
  49. package/packages/coding-agent/src/roles/skills.ts +0 -72
  50. package/packages/coding-agent/src/roles/sync.ts +0 -88
  51. package/packages/coding-agent/src/types.ts +0 -102
  52. package/packages/coding-agent/src/workspace.ts +0 -275
  53. package/packages/sandbox/README.md +0 -691
  54. package/packages/sandbox/bin/flowdular-sandbox.mjs +0 -393
  55. package/packages/sandbox/dist/client/assets/App-ClMOrpyg.js +0 -11
  56. package/packages/sandbox/dist/client/assets/PreviewHost-BHiHrfbQ.js +0 -1
  57. package/packages/sandbox/dist/client/assets/auto-buGEq6de.js +0 -3
  58. package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  59. package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  60. package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
  61. package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
  62. package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  63. package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  64. package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
  65. package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
  66. package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  67. package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  68. package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  69. package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  70. package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  71. package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  72. package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
  73. package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
  74. package/packages/sandbox/dist/client/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  75. package/packages/sandbox/dist/client/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  76. package/packages/sandbox/dist/client/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
  77. package/packages/sandbox/dist/client/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
  78. package/packages/sandbox/dist/client/assets/ibm-plex-sans-cyrillic-ext-wght-normal-d45eAU9y.woff2 +0 -0
  79. package/packages/sandbox/dist/client/assets/ibm-plex-sans-cyrillic-wght-normal-BAAhND-U.woff2 +0 -0
  80. package/packages/sandbox/dist/client/assets/ibm-plex-sans-greek-wght-normal-CmyJS8uq.woff2 +0 -0
  81. package/packages/sandbox/dist/client/assets/ibm-plex-sans-latin-ext-wght-normal-CIII54If.woff2 +0 -0
  82. package/packages/sandbox/dist/client/assets/ibm-plex-sans-latin-wght-normal-IvpUvPa2.woff2 +0 -0
  83. package/packages/sandbox/dist/client/assets/ibm-plex-sans-vietnamese-wght-normal-Dg1JeJN0.woff2 +0 -0
  84. package/packages/sandbox/dist/client/assets/index-CvwhlQ87.js +0 -4
  85. package/packages/sandbox/dist/client/assets/styles-BPWaGf55.js +0 -3
  86. package/packages/sandbox/dist/client/assets/styles-Dx1_KOag.css +0 -1
  87. package/packages/sandbox/dist/client/favicon.svg +0 -1
  88. package/packages/sandbox/dist/server/assets/auto-CVGNYyb3.js +0 -12785
  89. package/packages/sandbox/dist/server/assets/chunk-DDJLRBDX-3OUNo51g.js +0 -4125
  90. package/packages/sandbox/dist/server/assets/nodefs-DVSI7kG0.js +0 -24
  91. package/packages/sandbox/dist/server/assets/opfs-ahp-D56iokt1.js +0 -388
  92. package/packages/sandbox/dist/server/assets/token-Bvaq7Anz.js +0 -55
  93. package/packages/sandbox/dist/server/assets/token-util-BqSvdIPQ.js +0 -3
  94. package/packages/sandbox/dist/server/assets/token-util-CktFFT4z.js +0 -494
  95. package/packages/sandbox/dist/server/entry.js +0 -91671
  96. package/packages/sandbox/dist/server/favicon.svg +0 -1
  97. package/packages/sandbox/dist/server/index.html +0 -161
  98. package/packages/sandbox/index.html +0 -159
  99. package/packages/sandbox/octane.config.ts +0 -36
  100. package/packages/sandbox/package.json +0 -83
  101. package/packages/sandbox/public/favicon.svg +0 -1
  102. package/packages/sandbox/public/og.png +0 -0
  103. package/packages/sandbox/src/App.tsrx +0 -924
  104. package/packages/sandbox/src/client/ApprovalHandoff.tsrx +0 -39
  105. package/packages/sandbox/src/client/ChatPane.tsrx +0 -694
  106. package/packages/sandbox/src/client/ConnectView.tsrx +0 -86
  107. package/packages/sandbox/src/client/DiffPane.tsrx +0 -150
  108. package/packages/sandbox/src/client/EjectModal.tsrx +0 -571
  109. package/packages/sandbox/src/client/GateResults.tsrx +0 -60
  110. package/packages/sandbox/src/client/GitHubSettingsModal.tsrx +0 -219
  111. package/packages/sandbox/src/client/NewSessionForm.tsrx +0 -214
  112. package/packages/sandbox/src/client/SessionBar.tsrx +0 -151
  113. package/packages/sandbox/src/client/SessionPicker.tsrx +0 -472
  114. package/packages/sandbox/src/client/SpecEditor.tsrx +0 -109
  115. package/packages/sandbox/src/client/SpecReviewCard.tsrx +0 -321
  116. package/packages/sandbox/src/client/WorkCard.tsrx +0 -128
  117. package/packages/sandbox/src/client/WorkbenchModal.tsrx +0 -161
  118. package/packages/sandbox/src/client/WorkspaceMenu.tsrx +0 -100
  119. package/packages/sandbox/src/client/api.ts +0 -797
  120. package/packages/sandbox/src/client/attachments.ts +0 -73
  121. package/packages/sandbox/src/client/dashboard.ts +0 -46
  122. package/packages/sandbox/src/client/github-settings.ts +0 -48
  123. package/packages/sandbox/src/client/highlight.ts +0 -105
  124. package/packages/sandbox/src/client/i18n.ts +0 -32
  125. package/packages/sandbox/src/client/locales/en.json +0 -466
  126. package/packages/sandbox/src/client/locales/pl.json +0 -466
  127. package/packages/sandbox/src/client/session-labels.ts +0 -77
  128. package/packages/sandbox/src/client/state.ts +0 -108
  129. package/packages/sandbox/src/index.ts +0 -10
  130. package/packages/sandbox/src/preview/PreviewHost.tsrx +0 -373
  131. package/packages/sandbox/src/preview/load-module.ts +0 -16
  132. package/packages/sandbox/src/server/attachments.ts +0 -308
  133. package/packages/sandbox/src/server/auto-review.ts +0 -181
  134. package/packages/sandbox/src/server/checkpoints.ts +0 -90
  135. package/packages/sandbox/src/server/config.ts +0 -460
  136. package/packages/sandbox/src/server/dashboard.ts +0 -245
  137. package/packages/sandbox/src/server/delivery/configuration.ts +0 -193
  138. package/packages/sandbox/src/server/delivery/git-pr.ts +0 -1473
  139. package/packages/sandbox/src/server/delivery/index.ts +0 -83
  140. package/packages/sandbox/src/server/delivery/local.ts +0 -151
  141. package/packages/sandbox/src/server/delivery/official-modules.ts +0 -516
  142. package/packages/sandbox/src/server/delivery/plan.ts +0 -218
  143. package/packages/sandbox/src/server/delivery/policies.ts +0 -239
  144. package/packages/sandbox/src/server/delivery/record.ts +0 -41
  145. package/packages/sandbox/src/server/delivery/steps.ts +0 -464
  146. package/packages/sandbox/src/server/delivery/types.ts +0 -158
  147. package/packages/sandbox/src/server/dependencies.ts +0 -88
  148. package/packages/sandbox/src/server/diff.ts +0 -230
  149. package/packages/sandbox/src/server/gates.ts +0 -388
  150. package/packages/sandbox/src/server/index.ts +0 -139
  151. package/packages/sandbox/src/server/path-guard.ts +0 -282
  152. package/packages/sandbox/src/server/planning.ts +0 -749
  153. package/packages/sandbox/src/server/platform-client.ts +0 -189
  154. package/packages/sandbox/src/server/preview-database-host.ts +0 -340
  155. package/packages/sandbox/src/server/preview-database-protocol.ts +0 -163
  156. package/packages/sandbox/src/server/preview-database-proxy.ts +0 -356
  157. package/packages/sandbox/src/server/preview-database.ts +0 -109
  158. package/packages/sandbox/src/server/preview-ipc.ts +0 -15
  159. package/packages/sandbox/src/server/preview-modules.ts +0 -83
  160. package/packages/sandbox/src/server/preview-revision.ts +0 -44
  161. package/packages/sandbox/src/server/preview-runtime.ts +0 -467
  162. package/packages/sandbox/src/server/preview-worker-manager.ts +0 -436
  163. package/packages/sandbox/src/server/preview-worker.ts +0 -168
  164. package/packages/sandbox/src/server/reference.ts +0 -251
  165. package/packages/sandbox/src/server/routes.ts +0 -2258
  166. package/packages/sandbox/src/server/runtime.ts +0 -188
  167. package/packages/sandbox/src/server/session-owner.ts +0 -23
  168. package/packages/sandbox/src/server/sessions.ts +0 -849
  169. package/packages/sandbox/src/server/spec.ts +0 -471
  170. package/packages/sandbox/src/server/turn-lifecycle.ts +0 -56
  171. package/packages/sandbox/src/server/turns.ts +0 -993
  172. package/packages/sandbox/src/server/workspace-install.ts +0 -266
  173. package/packages/sandbox/src/server/workspace-root.ts +0 -73
  174. package/packages/sandbox/src/styles.css +0 -1573
  175. package/packages/sandbox/vite.config.ts +0 -133
@@ -1,219 +0,0 @@
1
- import { useState } from 'octane';
2
- import { Alert, Button, Drawer, FormField } from '@flowdular/sdk/ui';
3
- import { githubSettingsInput } from './github-settings.ts';
4
- import type { SafeSandboxConfiguration } from '../server/config.ts';
5
- import { useTranslation } from './i18n.ts';
6
-
7
- export interface GitHubSettingsInput {
8
- readonly githubEnabled: boolean;
9
- readonly githubOverridesProject: boolean;
10
- readonly githubRemote: string;
11
- readonly githubRepository: string;
12
- readonly githubBaseBranch: string;
13
- readonly githubBranchPrefix: string;
14
- readonly githubMode: 'auto' | 'direct' | 'fork';
15
- readonly githubForkOwner: string;
16
- readonly githubReviewers: readonly string[];
17
- readonly githubToken?: string;
18
- readonly githubClearToken?: boolean;
19
- }
20
-
21
- export interface GitHubSettingsModalProps {
22
- readonly configuration: SafeSandboxConfiguration;
23
- readonly busy: boolean;
24
- readonly error: string;
25
- readonly onSave: (input: GitHubSettingsInput) => void;
26
- readonly onClose: () => void;
27
- }
28
-
29
- export function GitHubSettingsModal(props: GitHubSettingsModalProps) @{
30
- const { t } = useTranslation();
31
- const github = props.configuration.github;
32
- const [enabled, setEnabled] = useState(github.enabled);
33
- const [custom, setCustom] = useState(github.overridesProject);
34
- const submit = (event: SubmitEvent) => {
35
- event.preventDefault();
36
- props.onSave(
37
- githubSettingsInput(
38
- new FormData(event.currentTarget as HTMLFormElement),
39
- github,
40
- ),
41
- );
42
- };
43
-
44
- <Drawer
45
- open={true}
46
- title={t('sandbox.github.title')}
47
- subtitle={t('sandbox.github.description')}
48
- width="lg"
49
- onClose={() => {
50
- if (!props.busy) props.onClose();
51
- }}
52
- >
53
- <form class="ui-drawer__form" onSubmit={submit}>
54
- <div class="ui-drawer__body">
55
- <div class="ui-form">
56
- <label class="ui-checkbox">
57
- <input
58
- type="checkbox"
59
- name="githubEnabled"
60
- checked={enabled}
61
- onChange={(event) => setEnabled(event.currentTarget.checked)}
62
- />
63
- <span>{t('sandbox.github.offer')}</span>
64
- </label>
65
- <Alert tone="info">{t('sandbox.github.safety')}</Alert>
66
- <label class="ui-checkbox">
67
- <input
68
- type="checkbox"
69
- name="githubOverridesProject"
70
- checked={custom}
71
- onChange={(event) => setCustom(event.currentTarget.checked)}
72
- />
73
- <span>{t('sandbox.github.overrideProject')}</span>
74
- </label>
75
- @if (!custom) {
76
- <p class="ui-help">{t('sandbox.github.projectHelp')}</p>
77
- } @else {
78
- <div class="ui-form__section">
79
- <FormField
80
- label={t('sandbox.github.repository.label')}
81
- help={t('sandbox.github.repository.help')}
82
- >
83
- <input
84
- class="ui-input ui-mono"
85
- name="githubRepository"
86
- defaultValue={github.repository ?? ''}
87
- placeholder={t('sandbox.github.repository.placeholder')}
88
- maxlength={200}
89
- />
90
- </FormField>
91
- <FormField
92
- label={t('sandbox.github.mode.label')}
93
- help={t('sandbox.github.mode.help')}
94
- >
95
- <select
96
- class="ui-select"
97
- name="githubMode"
98
- defaultValue={github.mode}
99
- >
100
- <option value="auto">{t('sandbox.github.mode.auto')}</option>
101
- <option value="direct">{t(
102
- 'sandbox.github.mode.direct',
103
- )}</option>
104
- <option value="fork">{t('sandbox.github.mode.fork')}</option>
105
- </select>
106
- </FormField>
107
- <FormField
108
- label={t('sandbox.github.reviewers.label')}
109
- help={t('sandbox.github.reviewers.help')}
110
- >
111
- <input
112
- class="ui-input ui-mono"
113
- name="githubReviewers"
114
- defaultValue={github.reviewers.join(', ')}
115
- maxlength={1000}
116
- />
117
- </FormField>
118
- <details>
119
- <summary class="ui-label">{t(
120
- 'sandbox.github.advanced',
121
- )}</summary>
122
- <div class="ui-form">
123
- <div class="ui-form__row">
124
- <FormField
125
- label={t('sandbox.github.remote.label')}
126
- help={t('sandbox.github.remote.help')}
127
- >
128
- <input
129
- class="ui-input ui-mono"
130
- name="githubRemote"
131
- defaultValue={github.remote}
132
- maxlength={120}
133
- />
134
- </FormField>
135
- <FormField
136
- label={t('sandbox.github.base.label')}
137
- help={t('sandbox.github.base.help')}
138
- >
139
- <input
140
- class="ui-input ui-mono"
141
- name="githubBaseBranch"
142
- defaultValue={github.baseBranch}
143
- maxlength={120}
144
- />
145
- </FormField>
146
- </div>
147
- <div class="ui-form__row">
148
- <FormField label={t('sandbox.github.prefix.label')}>
149
- <input
150
- class="ui-input ui-mono"
151
- name="githubBranchPrefix"
152
- defaultValue={github.branchPrefix}
153
- maxlength={120}
154
- />
155
- </FormField>
156
- <FormField
157
- label={t('sandbox.github.forkOwner.label')}
158
- help={t('sandbox.github.forkOwner.help')}
159
- >
160
- <input
161
- class="ui-input ui-mono"
162
- name="githubForkOwner"
163
- defaultValue={github.forkOwner ?? ''}
164
- maxlength={120}
165
- />
166
- </FormField>
167
- </div>
168
- </div>
169
- </details>
170
- </div>
171
- }
172
- <div class="ui-form__section">
173
- <div class="ui-form__section-head">
174
- <b>{t('sandbox.github.access')}</b>
175
- <small>{t('sandbox.github.accessHelp')}</small>
176
- </div>
177
- <FormField
178
- label={t('sandbox.github.token.label')}
179
- help={github.tokenFingerprint
180
- ? t('sandbox.github.token.stored', {
181
- fingerprint: github.tokenFingerprint,
182
- })
183
- : t('sandbox.github.token.help')}
184
- >
185
- <input
186
- class="ui-input ui-mono"
187
- name="githubToken"
188
- type="password"
189
- autocomplete="new-password"
190
- placeholder={t('sandbox.github.token.placeholder')}
191
- maxlength={16384}
192
- />
193
- </FormField>
194
- @if (github.tokenFingerprint) {
195
- <label class="ui-checkbox">
196
- <input type="checkbox" name="githubClearToken" />
197
- <span>{t('sandbox.github.token.remove')}</span>
198
- </label>
199
- }
200
- </div>
201
- @if (props.error) {
202
- <Alert>{props.error}</Alert>
203
- }
204
- </div>
205
- </div>
206
- <div class="ui-drawer__foot">
207
- <small>{t('sandbox.github.saveNote')}</small>
208
- <div class="ui-form__actions">
209
- <Button onClick={props.onClose} disabled={props.busy}>{t(
210
- 'sandbox.common.cancel',
211
- )}</Button>
212
- <Button type="submit" variant="primary" disabled={props.busy}>
213
- {props.busy ? t('sandbox.common.saving') : t('sandbox.github.save')}
214
- </Button>
215
- </div>
216
- </div>
217
- </form>
218
- </Drawer>
219
- }
@@ -1,214 +0,0 @@
1
- import { useEffect, useRef, useState } from 'octane';
2
- import { Button, Icon } from '@flowdular/sdk/ui';
3
- import type { DriverSummary } from './api.ts';
4
- import {
5
- ATTACH_ACCEPT,
6
- attachmentKind,
7
- attachmentRefusal,
8
- formatBytes,
9
- } from './attachments.ts';
10
- import { useTranslation } from './i18n.ts';
11
-
12
- export interface NewSessionFormProps {
13
- readonly drivers: readonly DriverSummary[];
14
- readonly driver: string;
15
- readonly busy: boolean;
16
- readonly onDriver: (driver: string) => void;
17
- readonly onCreate: (brief: string, driver: string, attachments: readonly File[]) => void;
18
- }
19
- /* A file the operator attached before the session exists. It is uploaded to
20
- the new session id right after the session is created. */
21
- interface PickedFile {
22
- readonly key: string;
23
- readonly file: File;
24
- readonly kind: 'image' | 'file';
25
- /* Object URL for an image thumbnail, empty otherwise. Revoked on removal
26
- and when the screen goes away. */
27
- readonly url: string;
28
- }
29
-
30
- export function NewSessionForm(props: NewSessionFormProps) @{
31
- const { t } = useTranslation();
32
- const offered = props.drivers.filter((driver) => driver.offered);
33
- const selected =
34
- offered.find((driver) => driver.id === props.driver) ?? offered[0] ?? null;
35
- const attachInput = useRef<HTMLInputElement | null>(null);
36
- const opened = useRef<string[]>([]);
37
- const [picked, setPicked] = useState<readonly PickedFile[]>([]);
38
- const [attachError, setAttachError] = useState('');
39
-
40
- /* Thumbnails hold object URLs; nothing else releases them, so the screen
41
- does it when it goes away. */
42
- useEffect(
43
- () => () => {
44
- for (const url of opened.current) URL.revokeObjectURL(url);
45
- },
46
- [],
47
- );
48
-
49
- const addFiles = (chosen: readonly File[]) => {
50
- const next = [...picked];
51
- let refusal = '';
52
- for (const file of chosen) {
53
- const reason = attachmentRefusal(file, next.length);
54
- if (reason) {
55
- refusal = reason;
56
- continue;
57
- }
58
- const kind = attachmentKind(file);
59
- const url =
60
- kind === 'image' ? URL.createObjectURL(file) : '';
61
- if (url) opened.current.push(url);
62
- next.push({
63
- key: `${file.name}-${Date.now()}-${Math.random().toString(36).slice(
64
- 2,
65
- 8,
66
- )}`,
67
- file,
68
- kind,
69
- url,
70
- });
71
- }
72
- setPicked(next);
73
- setAttachError(refusal);
74
- };
75
-
76
- const removeFile = (key: string) => {
77
- const found = picked.find((item) => item.key === key);
78
- if (found?.url) URL.revokeObjectURL(found.url);
79
- setPicked(picked.filter((item) => item.key !== key));
80
- };
81
-
82
- const onPaste = (event: ClipboardEvent) => {
83
- const files = event.clipboardData?.files;
84
- if (!files || files.length === 0) return;
85
- const images = Array.from(files).filter(
86
- (file) => file.type.startsWith('image/'),
87
- );
88
- if (images.length === 0) return;
89
- event.preventDefault();
90
- addFiles(images);
91
- };
92
-
93
- const onPick = (event: Event & { currentTarget: HTMLInputElement }) => {
94
- const chosen = event.currentTarget.files;
95
- if (chosen && chosen.length > 0) addFiles(Array.from(chosen));
96
- event.currentTarget.value = '';
97
- };
98
-
99
- const submit = (event: SubmitEvent) => {
100
- event.preventDefault();
101
- const form = new FormData(event.currentTarget as HTMLFormElement);
102
- const brief = form.get('brief');
103
- if (typeof brief === 'string' && brief.trim().length > 7 && selected) {
104
- props.onCreate(
105
- brief.trim(),
106
- selected.id,
107
- picked.map((item) => item.file),
108
- );
109
- }
110
- };
111
-
112
- <form class="ui-drawer__form" onSubmit={submit}>
113
- <div class="ui-drawer__body">
114
- <div class="ui-form">
115
- @if (picked.length > 0 || attachError) {
116
- <div class="chat__attachments">
117
- @for (const item of picked; key item.key) {
118
- <div class="chat__attachment">
119
- @if (item.kind === 'image') {
120
- <img
121
- class="chat__attachment-thumb"
122
- src={item.url}
123
- alt={item.file.name}
124
- />
125
- } @else {
126
- <span
127
- class="chat__attachment-thumb chat__attachment-thumb--file"
128
- >
129
- <Icon name="file-text" size={18} />
130
- </span>
131
- }
132
- <span class="chat__attachment-info">
133
- <span class="chat__attachment-name">{item.file.name}</span>
134
- <span class="chat__attachment-size">{formatBytes(
135
- item.file.size,
136
- )}</span>
137
- </span>
138
- <button
139
- class="chat__attachment-remove"
140
- type="button"
141
- aria-label={t('sandbox.attachments.remove', {
142
- name: item.file.name,
143
- })}
144
- onClick={() => removeFile(item.key)}
145
- >
146
- <Icon name="x" size={12} />
147
- </button>
148
- </div>
149
- }
150
- @if (attachError) {
151
- <span class="chat__attachment-error">{attachError}</span>
152
- }
153
- </div>
154
- }
155
- <textarea
156
- class="ui-textarea"
157
- aria-label={t('sandbox.sessions.brief.placeholder')}
158
- minlength={8}
159
- maxlength={20000}
160
- name="brief"
161
- rows={4}
162
- placeholder={t('sandbox.sessions.brief.placeholder')}
163
- required
164
- onPaste={onPaste}
165
- ></textarea>
166
- </div>
167
- </div>
168
- <div class="ui-drawer__foot">
169
- <button
170
- class="chat__attach"
171
- type="button"
172
- aria-label={t('sandbox.attachments.action')}
173
- title={t('sandbox.attachments.help')}
174
- onClick={() => attachInput.current?.click()}
175
- >
176
- <Icon name="plus" size={16} />
177
- </button>
178
- <input
179
- ref={attachInput}
180
- class="chat__attach-input"
181
- type="file"
182
- multiple
183
- accept={ATTACH_ACCEPT}
184
- onChange={onPick}
185
- />
186
- <select
187
- class="chat__picker"
188
- aria-label={t('sandbox.sessions.driver.label')}
189
- value={selected?.id ?? ''}
190
- disabled={offered.length === 0}
191
- onChange={(event) => props.onDriver(event.currentTarget.value)}
192
- >
193
- @for (const driver of offered; key driver.id) {
194
- <option value={driver.id}>{driver.label}</option>
195
- }
196
- </select>
197
- <span class="sandbox__spacer"></span>
198
- <Button
199
- size="sm"
200
- variant="primary"
201
- type="submit"
202
- disabled={props.busy || offered.length === 0}
203
- >
204
- <Icon name="play" size={14} />
205
- {props.busy
206
- ? t('sandbox.sessions.planning')
207
- : t('sandbox.sessions.start')}
208
- </Button>
209
- </div>
210
- <p class="ui-note">
211
- {selected ? selected.description : t('sandbox.sessions.driver.none')}
212
- </p>
213
- </form>
214
- }
@@ -1,151 +0,0 @@
1
- import { useState } from 'octane';
2
- import { Button, Icon, Tag } from '@flowdular/sdk/ui';
3
- import type { SandboxSession } from '../server/sessions.ts';
4
- import type { WorkspaceModuleSummary } from './api.ts';
5
- import { useTranslation } from './i18n.ts';
6
-
7
- export interface SessionBarProps {
8
- readonly session: SandboxSession;
9
- readonly changes: number;
10
- readonly busy: boolean;
11
- /* The module the panes show; empty means every module of the session. */
12
- readonly activeModule: string;
13
- /* Every module of the workspace, so the session can take another one in. */
14
- readonly workspaceModules: readonly WorkspaceModuleSummary[];
15
- readonly onModule: (directory: string) => void;
16
- readonly onAddModule: (moduleId: string) => void;
17
- readonly onBack: () => void;
18
- readonly onGates: () => void;
19
- readonly onChanges: () => void;
20
- readonly onPreview: () => void;
21
- readonly onEject: () => void;
22
- }
23
-
24
- function stateTone(
25
- state: SandboxSession['state'],
26
- ): 'success' | 'warning' | 'danger' | 'info' | 'neutral' {
27
- if (state === 'accepted') return 'success';
28
- if (state === 'failed' || state === 'blocked') return 'danger';
29
- if (state === 'awaiting-approval' || state === 'validating') return 'warning';
30
- if (state === 'draft') return 'neutral';
31
- return 'info';
32
- }
33
-
34
- /* Everything here belongs to one module session. The application bar above
35
- stays global, so a session action can never look like a workspace action. */
36
- export function SessionBar(props: SessionBarProps) @{
37
- const { t } = useTranslation();
38
- const delivered = Boolean(props.session.ejectedAt);
39
- const modules = props.session.modules;
40
- const held = new Set(modules.map((module) => module.id));
41
- const candidates = props.workspaceModules.filter(
42
- (module) => !held.has(module.id),
43
- );
44
- const [adding, setAdding] = useState(false);
45
- const [chosen, setChosen] = useState('');
46
-
47
- <div class="session-bar">
48
- <button class="session-bar__back" type="button" onClick={props.onBack}>
49
- <Icon name="chevron-down" size={14} />
50
- {t('sandbox.session.back')}
51
- </button>
52
- <span class="session-bar__title">
53
- <b>{props.session.title}</b>
54
- <small class="ui-mono">
55
- {modules.length === 1
56
- ? props.session.moduleId
57
- : t('sandbox.session.modules', { count: modules.length })}
58
- </small>
59
- </span>
60
- <Tag tone={stateTone(props.session.state)} dot>
61
- {delivered
62
- ? t('sandbox.session.state.delivered')
63
- : t('sandbox.session.state.' + props.session.state)}
64
- </Tag>
65
- @if (modules.length > 1) {
66
- <select
67
- class="chat__picker"
68
- aria-label={t('sandbox.session.module.visible')}
69
- value={props.activeModule}
70
- onChange={(event) => props.onModule(event.currentTarget.value)}
71
- >
72
- <option value="">{t('sandbox.session.module.all')}</option>
73
- @for (const module of modules; key module.id) {
74
- <option value={module.directory}>{module.id}</option>
75
- }
76
- </select>
77
- }
78
- @if (adding) {
79
- <>
80
- <select
81
- class="chat__picker"
82
- aria-label={t('sandbox.session.module.addLabel')}
83
- value={chosen}
84
- onChange={(event) => setChosen(event.currentTarget.value)}
85
- >
86
- <option value="">{t('sandbox.session.module.choose')}</option>
87
- @for (const module of candidates; key module.id) {
88
- <option value={module.id}>{module.id}</option>
89
- }
90
- </select>
91
- <Button
92
- size="sm"
93
- variant="primary"
94
- disabled={props.busy || !chosen}
95
- onClick={() => {
96
- props.onAddModule(chosen);
97
- setChosen('');
98
- setAdding(false);
99
- }}
100
- >{t('sandbox.common.add')}</Button>
101
- <Button size="sm" variant="ghost" onClick={() => setAdding(false)}>{t(
102
- 'sandbox.common.cancel',
103
- )}</Button>
104
- </>
105
- } @else if (!delivered && candidates.length > 0) {
106
- <button
107
- class="session-bar__add"
108
- type="button"
109
- disabled={props.busy}
110
- onClick={() => setAdding(true)}
111
- >
112
- <Icon name="plus" size={12} />
113
- {t('sandbox.session.module.add')}
114
- </button>
115
- }
116
- <span class="sandbox__spacer"></span>
117
- <Button size="sm" variant="ghost" onClick={props.onPreview}>
118
- <Icon name="play" size={14} />
119
- {t('sandbox.work.preview')}
120
- </Button>
121
- <Button
122
- size="sm"
123
- variant="ghost"
124
- onClick={props.onGates}
125
- disabled={props.busy}
126
- >
127
- <Icon name="check" size={14} />
128
- {t('sandbox.session.gates')}
129
- </Button>
130
- <Button
131
- size="sm"
132
- variant="ghost"
133
- onClick={props.onChanges}
134
- disabled={props.changes === 0}
135
- >
136
- <Icon name="code" size={14} />
137
- {t('sandbox.session.changes', { count: props.changes })}
138
- </Button>
139
- <Button
140
- size="sm"
141
- variant={delivered ? 'ghost' : 'secondary'}
142
- onClick={props.onEject}
143
- disabled={props.busy || props.changes === 0}
144
- >
145
- <Icon name={delivered ? 'check' : 'download'} size={14} />
146
- {delivered
147
- ? t('sandbox.session.deliverAgain')
148
- : t('sandbox.session.eject')}
149
- </Button>
150
- </div>
151
- }