@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.
- package/.ai/skills/test-hardening/SKILL.md +1 -1
- package/README.md +69 -12
- package/assets/flowdular-banner.png +0 -0
- package/modules/agents/package.json +1 -1
- package/modules/auth/package.json +1 -1
- package/modules/auth/src/client/AuthenticationCore.tsrx +3 -1
- package/modules/auth/src/server/endpoints.ts +3 -1
- package/modules/auth/src/server/runtime.ts +4 -0
- package/modules/automations/package.json +1 -1
- package/modules/automations-workflows-integration/package.json +1 -1
- package/modules/profile/package.json +1 -1
- package/modules/sandbox/package.json +1 -1
- package/modules/system/package.json +1 -1
- package/modules/users/package.json +1 -1
- package/modules/workflows/package.json +1 -1
- package/package.json +5 -13
- package/packages/ai-provider/package.json +1 -1
- package/packages/cli-protocol/package.json +1 -1
- package/packages/client/package.json +1 -1
- package/packages/client/src/routing.ts +39 -2
- package/packages/client/src/shell/BrandMark.tsrx +5 -1
- package/packages/client/src/shell/navigation.ts +3 -1
- package/packages/client/src/state.ts +6 -2
- package/packages/contracts/package.json +1 -1
- package/packages/contracts/schemas/project.schema.json +16 -42
- package/packages/database/package.json +1 -1
- package/packages/database-pglite/package.json +2 -2
- package/packages/database-pglite/src/driver.ts +21 -3
- package/packages/database-testing/package.json +1 -1
- package/packages/dev-console/package.json +1 -1
- package/packages/harness/package.json +1 -1
- package/packages/kernel/package.json +1 -1
- package/packages/server/package.json +1 -1
- package/packages/server/src/application-routes.ts +80 -8
- package/packages/server/src/index.ts +4 -1
- package/packages/server/src/web-html.ts +9 -5
- package/packages/server/src/web.ts +59 -7
- package/packages/ui/package.json +1 -1
- package/packages/coding-agent/package.json +0 -44
- package/packages/coding-agent/src/drivers/byok.ts +0 -322
- package/packages/coding-agent/src/drivers/claude-code.ts +0 -290
- package/packages/coding-agent/src/drivers/codex.ts +0 -281
- package/packages/coding-agent/src/index.ts +0 -56
- package/packages/coding-agent/src/registry.ts +0 -119
- package/packages/coding-agent/src/roles/contract.ts +0 -60
- package/packages/coding-agent/src/roles/defaults.ts +0 -134
- package/packages/coding-agent/src/roles/handoff.ts +0 -41
- package/packages/coding-agent/src/roles/registry.ts +0 -156
- package/packages/coding-agent/src/roles/skills.ts +0 -72
- package/packages/coding-agent/src/roles/sync.ts +0 -88
- package/packages/coding-agent/src/types.ts +0 -102
- package/packages/coding-agent/src/workspace.ts +0 -275
- package/packages/sandbox/README.md +0 -691
- package/packages/sandbox/bin/flowdular-sandbox.mjs +0 -393
- package/packages/sandbox/dist/client/assets/App-ClMOrpyg.js +0 -11
- package/packages/sandbox/dist/client/assets/PreviewHost-BHiHrfbQ.js +0 -1
- package/packages/sandbox/dist/client/assets/auto-buGEq6de.js +0 -3
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-sans-cyrillic-ext-wght-normal-d45eAU9y.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-sans-cyrillic-wght-normal-BAAhND-U.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-sans-greek-wght-normal-CmyJS8uq.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-sans-latin-ext-wght-normal-CIII54If.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-sans-latin-wght-normal-IvpUvPa2.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/ibm-plex-sans-vietnamese-wght-normal-Dg1JeJN0.woff2 +0 -0
- package/packages/sandbox/dist/client/assets/index-CvwhlQ87.js +0 -4
- package/packages/sandbox/dist/client/assets/styles-BPWaGf55.js +0 -3
- package/packages/sandbox/dist/client/assets/styles-Dx1_KOag.css +0 -1
- package/packages/sandbox/dist/client/favicon.svg +0 -1
- package/packages/sandbox/dist/server/assets/auto-CVGNYyb3.js +0 -12785
- package/packages/sandbox/dist/server/assets/chunk-DDJLRBDX-3OUNo51g.js +0 -4125
- package/packages/sandbox/dist/server/assets/nodefs-DVSI7kG0.js +0 -24
- package/packages/sandbox/dist/server/assets/opfs-ahp-D56iokt1.js +0 -388
- package/packages/sandbox/dist/server/assets/token-Bvaq7Anz.js +0 -55
- package/packages/sandbox/dist/server/assets/token-util-BqSvdIPQ.js +0 -3
- package/packages/sandbox/dist/server/assets/token-util-CktFFT4z.js +0 -494
- package/packages/sandbox/dist/server/entry.js +0 -91671
- package/packages/sandbox/dist/server/favicon.svg +0 -1
- package/packages/sandbox/dist/server/index.html +0 -161
- package/packages/sandbox/index.html +0 -159
- package/packages/sandbox/octane.config.ts +0 -36
- package/packages/sandbox/package.json +0 -83
- package/packages/sandbox/public/favicon.svg +0 -1
- package/packages/sandbox/public/og.png +0 -0
- package/packages/sandbox/src/App.tsrx +0 -924
- package/packages/sandbox/src/client/ApprovalHandoff.tsrx +0 -39
- package/packages/sandbox/src/client/ChatPane.tsrx +0 -694
- package/packages/sandbox/src/client/ConnectView.tsrx +0 -86
- package/packages/sandbox/src/client/DiffPane.tsrx +0 -150
- package/packages/sandbox/src/client/EjectModal.tsrx +0 -571
- package/packages/sandbox/src/client/GateResults.tsrx +0 -60
- package/packages/sandbox/src/client/GitHubSettingsModal.tsrx +0 -219
- package/packages/sandbox/src/client/NewSessionForm.tsrx +0 -214
- package/packages/sandbox/src/client/SessionBar.tsrx +0 -151
- package/packages/sandbox/src/client/SessionPicker.tsrx +0 -472
- package/packages/sandbox/src/client/SpecEditor.tsrx +0 -109
- package/packages/sandbox/src/client/SpecReviewCard.tsrx +0 -321
- package/packages/sandbox/src/client/WorkCard.tsrx +0 -128
- package/packages/sandbox/src/client/WorkbenchModal.tsrx +0 -161
- package/packages/sandbox/src/client/WorkspaceMenu.tsrx +0 -100
- package/packages/sandbox/src/client/api.ts +0 -797
- package/packages/sandbox/src/client/attachments.ts +0 -73
- package/packages/sandbox/src/client/dashboard.ts +0 -46
- package/packages/sandbox/src/client/github-settings.ts +0 -48
- package/packages/sandbox/src/client/highlight.ts +0 -105
- package/packages/sandbox/src/client/i18n.ts +0 -32
- package/packages/sandbox/src/client/locales/en.json +0 -466
- package/packages/sandbox/src/client/locales/pl.json +0 -466
- package/packages/sandbox/src/client/session-labels.ts +0 -77
- package/packages/sandbox/src/client/state.ts +0 -108
- package/packages/sandbox/src/index.ts +0 -10
- package/packages/sandbox/src/preview/PreviewHost.tsrx +0 -373
- package/packages/sandbox/src/preview/load-module.ts +0 -16
- package/packages/sandbox/src/server/attachments.ts +0 -308
- package/packages/sandbox/src/server/auto-review.ts +0 -181
- package/packages/sandbox/src/server/checkpoints.ts +0 -90
- package/packages/sandbox/src/server/config.ts +0 -460
- package/packages/sandbox/src/server/dashboard.ts +0 -245
- package/packages/sandbox/src/server/delivery/configuration.ts +0 -193
- package/packages/sandbox/src/server/delivery/git-pr.ts +0 -1473
- package/packages/sandbox/src/server/delivery/index.ts +0 -83
- package/packages/sandbox/src/server/delivery/local.ts +0 -151
- package/packages/sandbox/src/server/delivery/official-modules.ts +0 -516
- package/packages/sandbox/src/server/delivery/plan.ts +0 -218
- package/packages/sandbox/src/server/delivery/policies.ts +0 -239
- package/packages/sandbox/src/server/delivery/record.ts +0 -41
- package/packages/sandbox/src/server/delivery/steps.ts +0 -464
- package/packages/sandbox/src/server/delivery/types.ts +0 -158
- package/packages/sandbox/src/server/dependencies.ts +0 -88
- package/packages/sandbox/src/server/diff.ts +0 -230
- package/packages/sandbox/src/server/gates.ts +0 -388
- package/packages/sandbox/src/server/index.ts +0 -139
- package/packages/sandbox/src/server/path-guard.ts +0 -282
- package/packages/sandbox/src/server/planning.ts +0 -749
- package/packages/sandbox/src/server/platform-client.ts +0 -189
- package/packages/sandbox/src/server/preview-database-host.ts +0 -340
- package/packages/sandbox/src/server/preview-database-protocol.ts +0 -163
- package/packages/sandbox/src/server/preview-database-proxy.ts +0 -356
- package/packages/sandbox/src/server/preview-database.ts +0 -109
- package/packages/sandbox/src/server/preview-ipc.ts +0 -15
- package/packages/sandbox/src/server/preview-modules.ts +0 -83
- package/packages/sandbox/src/server/preview-revision.ts +0 -44
- package/packages/sandbox/src/server/preview-runtime.ts +0 -467
- package/packages/sandbox/src/server/preview-worker-manager.ts +0 -436
- package/packages/sandbox/src/server/preview-worker.ts +0 -168
- package/packages/sandbox/src/server/reference.ts +0 -251
- package/packages/sandbox/src/server/routes.ts +0 -2258
- package/packages/sandbox/src/server/runtime.ts +0 -188
- package/packages/sandbox/src/server/session-owner.ts +0 -23
- package/packages/sandbox/src/server/sessions.ts +0 -849
- package/packages/sandbox/src/server/spec.ts +0 -471
- package/packages/sandbox/src/server/turn-lifecycle.ts +0 -56
- package/packages/sandbox/src/server/turns.ts +0 -993
- package/packages/sandbox/src/server/workspace-install.ts +0 -266
- package/packages/sandbox/src/server/workspace-root.ts +0 -73
- package/packages/sandbox/src/styles.css +0 -1573
- 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
|
-
}
|