@parallelworks/ui 0.45.0 → 0.46.0
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/README.md +4 -4
- package/dist/ai/agent/permissions.d.ts +1 -1
- package/dist/ai/agent/permissions.js +1 -1
- package/dist/ai/ai.css +4 -4
- package/dist/ai/components/ChatStage.d.ts +1 -1
- package/dist/ai/components/ChatStage.js +1 -1
- package/dist/ai/core/pasteLanguage.d.ts +1 -1
- package/dist/ai/core/pasteLanguage.js +1 -1
- package/dist/brandIcons.js +1 -1
- package/dist/components/Loader.d.ts +1 -1
- package/dist/components/Provider.d.ts +3 -3
- package/dist/components/Provider.js +3 -3
- package/dist/components/StatusBadge.d.ts +2 -2
- package/dist/components/StatusBadge.js +1 -1
- package/dist/components/Toast.js +1 -1
- package/dist/components/comboboxItems.d.ts +1 -1
- package/dist/components/tone.d.ts +1 -1
- package/dist/editing.d.ts +1 -1
- package/dist/file-explorer/FileExplorerContext.js +11 -11
- package/dist/file-explorer/FilePreview/useObjectBlobUrl.js +4 -4
- package/dist/file-explorer/FilePreview/useObjectText.js +4 -4
- package/dist/file-explorer/FilePreview/usePresignedUrl.js +4 -4
- package/dist/file-explorer/lib/types.d.ts +2 -2
- package/dist/file-explorer/toasts.js +9 -9
- package/dist/form/utils/fieldItems.d.ts +1 -1
- package/dist/graph/SuggestionInput.d.ts +1 -1
- package/dist/graph/SuggestionInput.js +1 -1
- package/dist/graph/editorPrimitives.d.ts +2 -2
- package/dist/graph/editorPrimitives.js +1 -1
- package/dist/graph/editorStrings.d.ts +2 -2
- package/dist/graph/editorStrings.js +4 -4
- package/dist/graph/inputRefs.js +1 -1
- package/dist/icons.js +1 -1
- package/dist/logviewer/segments.d.ts +1 -1
- package/dist/logviewer/segments.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -300,8 +300,8 @@ root:
|
|
|
300
300
|
|
|
301
301
|
| Variable | Meaning |
|
|
302
302
|
| ------------------- | ----------------------------------------------------------------------- |
|
|
303
|
-
| `--app-inset-top` | The height of a fixed top bar.
|
|
304
|
-
| `--app-inset-start` | The width of a fixed sidebar on the start side.
|
|
303
|
+
| `--app-inset-top` | The height of a fixed top bar. Drawers and toasts start below it. |
|
|
304
|
+
| `--app-inset-start` | The width of a fixed sidebar on the start side. Toasts center over the page beside it, and a drawer at its widest leaves that strip, and never less than 2.5rem. Update it when the sidebar collapses. |
|
|
305
305
|
|
|
306
306
|
```ts
|
|
307
307
|
document.documentElement.style.setProperty('--app-inset-top', '56px')
|
|
@@ -340,7 +340,7 @@ name is a meaning, such as `RetryIcon` or `WarningIcon`, drawn from
|
|
|
340
340
|
stroke. Don't import `lucide-react` directly: a name here keeps one glyph per
|
|
341
341
|
meaning across the apps.
|
|
342
342
|
|
|
343
|
-
- **Size and
|
|
343
|
+
- **Size and color:** an icon is `1em` square in `currentColor`, so it follows
|
|
344
344
|
the text around it. A class such as `size-4` or `h-3 w-3` sets another size,
|
|
345
345
|
and `size` takes pixels.
|
|
346
346
|
- **Accessibility:** an icon is decoration, hidden from assistive technology,
|
|
@@ -362,7 +362,7 @@ meaning across the apps.
|
|
|
362
362
|
|
|
363
363
|
Logos of other companies' products, such as `GitHubIcon` or `UbuntuIcon`, are
|
|
364
364
|
in `@parallelworks/ui/brand-icons`. They take the same props, are filled in the
|
|
365
|
-
text's
|
|
365
|
+
text's color, and are only for naming the product. They come from Simple Icons
|
|
366
366
|
(CC0) and Bootstrap Icons (MIT), and the file carries their notices.
|
|
367
367
|
|
|
368
368
|
The `UI/Icons` story shows every icon and logo by name.
|
|
@@ -6,7 +6,7 @@ export declare const DEFAULT_PERMISSION_MODE: PermissionMode;
|
|
|
6
6
|
export declare function isNamedPermissionMode(mode: string | undefined): mode is NamedPermissionMode;
|
|
7
7
|
/** The daemon clamps a remote request to its owner's remoteMaxPermissionMode
|
|
8
8
|
* and refuses a mode it does not know, so a menu offers only what that
|
|
9
|
-
* machine will
|
|
9
|
+
* machine will honor. */
|
|
10
10
|
export declare function permissionModesUpTo(ceiling: string | undefined, accepted?: readonly string[] | undefined): PermissionMode[];
|
|
11
11
|
export declare function clampPermissionMode(mode: PermissionMode, ceiling: string | undefined, accepted?: readonly string[] | undefined): PermissionMode;
|
|
12
12
|
export {};
|
|
@@ -26,7 +26,7 @@ var LEGACY_PERMISSION_MODES = [
|
|
|
26
26
|
];
|
|
27
27
|
/** The daemon clamps a remote request to its owner's remoteMaxPermissionMode
|
|
28
28
|
* and refuses a mode it does not know, so a menu offers only what that
|
|
29
|
-
* machine will
|
|
29
|
+
* machine will honor. */
|
|
30
30
|
function permissionModesUpTo(ceiling, accepted = void 0) {
|
|
31
31
|
const max = isNamedPermissionMode(ceiling) ? PERMISSION_RANK[ceiling] : PERMISSION_RANK[DEFAULT_PERMISSION_MODE];
|
|
32
32
|
const known = accepted?.length ? accepted : LEGACY_PERMISSION_MODES;
|
package/dist/ai/ai.css
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
padding: 0.125em 0.375em;
|
|
56
56
|
background-color: color-mix(in oklab, var(--theme-app, #000) 8%, transparent);
|
|
57
57
|
border-radius: 0.25rem;
|
|
58
|
-
/* Identifiers carry a
|
|
58
|
+
/* Identifiers carry a color of their own so they read as code at a glance
|
|
59
59
|
* without the weight the plugin gives them: the theme's link blue, which
|
|
60
60
|
* every palette already keeps legible on its surfaces. */
|
|
61
61
|
color: var(--theme-link, #2f6fa8);
|
|
@@ -248,7 +248,7 @@
|
|
|
248
248
|
explicitly to ensure correct colors in all modes.
|
|
249
249
|
See: https://shiki.style/guide/dual-themes#class-based-dark-mode */
|
|
250
250
|
|
|
251
|
-
/* Light mode: the primary Shiki theme's
|
|
251
|
+
/* Light mode: the primary Shiki theme's colors (--sdm-c) on the block's own
|
|
252
252
|
surface, which stands in for the theme's background. */
|
|
253
253
|
.markdown [data-streamdown="code-block-body"] pre {
|
|
254
254
|
/* biome-ignore lint/complexity/noImportantStyles: the highlighter sets the background as an inline style */
|
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
color: var(--sdm-c, inherit);
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
/* Dark mode: the --shiki-dark
|
|
262
|
+
/* Dark mode: the --shiki-dark colors, again on the block's surface. */
|
|
263
263
|
.dark .markdown [data-streamdown="code-block-body"] pre {
|
|
264
264
|
/* biome-ignore lint/complexity/noImportantStyles: the highlighter sets the background as an inline style */
|
|
265
265
|
background-color: transparent !important;
|
|
@@ -369,7 +369,7 @@
|
|
|
369
369
|
|
|
370
370
|
/* The composer is one surface lifted off the page: a hairline of the ink for
|
|
371
371
|
* its edge, and a short, soft shadow that deepens while it has focus. The
|
|
372
|
-
* shadow is black on every scheme; an ink-
|
|
372
|
+
* shadow is black on every scheme; an ink-colored one would glow on dark. */
|
|
373
373
|
.chat-composer {
|
|
374
374
|
box-shadow:
|
|
375
375
|
0 0 0 1px color-mix(in oklab, var(--theme-app, #000) 12%, transparent),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
/** Where a new conversation starts: the heading and the composer together, a
|
|
3
|
-
* little above
|
|
3
|
+
* little above center, with what the surface offers next under the box. The
|
|
4
4
|
* composer keeps its place however much is under it, so surfaces that share
|
|
5
5
|
* the stage put it in the same spot; a long list scrolls instead. */
|
|
6
6
|
export default function ChatStage({ heading, subheading, children, }: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
//#region src/ai/components/ChatStage.tsx
|
|
3
3
|
/** Where a new conversation starts: the heading and the composer together, a
|
|
4
|
-
* little above
|
|
4
|
+
* little above center, with what the surface offers next under the box. The
|
|
5
5
|
* composer keeps its place however much is under it, so surfaces that share
|
|
6
6
|
* the stage put it in the same spot; a long list scrolls instead. */
|
|
7
7
|
function ChatStage({ heading, subheading, children }) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The highlighter's name for the language a paste is written in, or ''
|
|
2
|
-
* when it is not
|
|
2
|
+
* when it is not recognizably one. */
|
|
3
3
|
export declare function guessPasteLanguage(text: string): string;
|
|
4
4
|
/** The paste as a fenced code block in its language. The fence outruns any
|
|
5
5
|
* run of backticks inside the paste, so nothing in it can close the fence. */
|
|
@@ -17,7 +17,7 @@ function isJson(trimmed) {
|
|
|
17
17
|
var YAML_LINE = /^\s*(-\s+)?["']?[\w./-]+["']?\s*:(\s|$)|^\s*-(\s|$)|^---\s*$/;
|
|
18
18
|
var TOML_LINE = /^\s*\[{1,2}[\w."-]+\]{1,2}\s*$|^\s*[\w."-]+\s*=\s*\S/;
|
|
19
19
|
/** The highlighter's name for the language a paste is written in, or ''
|
|
20
|
-
* when it is not
|
|
20
|
+
* when it is not recognizably one. */
|
|
21
21
|
function guessPasteLanguage(text) {
|
|
22
22
|
const trimmed = text.trim();
|
|
23
23
|
if (!trimmed) return "";
|
package/dist/brandIcons.js
CHANGED
|
@@ -25,7 +25,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
25
25
|
*
|
|
26
26
|
* Each logo is its owner's trademark; use one only to refer to its product.
|
|
27
27
|
*/
|
|
28
|
-
/** A one-
|
|
28
|
+
/** A one-color logo in the text's color, at the text's size. */
|
|
29
29
|
function brand(viewBox, ...paths) {
|
|
30
30
|
function Icon({ title, size = "1em", color = "currentColor", strokeWidth: _strokeWidth, absoluteStrokeWidth: _absoluteStrokeWidth, nonScalingStroke: _nonScalingStroke, children, ...props }) {
|
|
31
31
|
const named = Boolean(title || props["aria-label"] || props["aria-labelledby"]);
|
|
@@ -5,7 +5,7 @@ export interface LoaderProps {
|
|
|
5
5
|
* loader is named with the provider's `loading` string. */
|
|
6
6
|
label?: string | undefined;
|
|
7
7
|
variant?: 'spinner' | 'logo';
|
|
8
|
-
/** Sits in a line of text or a button instead of
|
|
8
|
+
/** Sits in a line of text or a button instead of centering in its own row. */
|
|
9
9
|
inline?: boolean;
|
|
10
10
|
/** The spinner's color, such as `currentColor` inside a button. */
|
|
11
11
|
color?: string | undefined;
|
|
@@ -406,7 +406,7 @@ export interface UIStrings {
|
|
|
406
406
|
failedTitle: string;
|
|
407
407
|
unknownTitle: string;
|
|
408
408
|
unknownShort: string;
|
|
409
|
-
|
|
409
|
+
canceled: string;
|
|
410
410
|
completed: string;
|
|
411
411
|
failed: string;
|
|
412
412
|
totalSize: string;
|
|
@@ -417,9 +417,9 @@ export interface UIStrings {
|
|
|
417
417
|
expandDetails: string;
|
|
418
418
|
minimizeDetails: string;
|
|
419
419
|
sessionNotQueued: (id: string, status: string) => string;
|
|
420
|
-
|
|
420
|
+
allCanceled: (count: number) => string;
|
|
421
421
|
someUploaded: (count: number) => string;
|
|
422
|
-
|
|
422
|
+
someCanceled: (count: number) => string;
|
|
423
423
|
allUploaded: string;
|
|
424
424
|
failedSummary: (failed: number, total: number) => string;
|
|
425
425
|
failReasonGeneric: string;
|
|
@@ -404,7 +404,7 @@ var DEFAULTS = {
|
|
|
404
404
|
failedTitle: "Upload Failed",
|
|
405
405
|
unknownTitle: "Unknown Status",
|
|
406
406
|
unknownShort: "Unknown",
|
|
407
|
-
|
|
407
|
+
canceled: "Canceled",
|
|
408
408
|
completed: "Completed",
|
|
409
409
|
failed: "Failed",
|
|
410
410
|
totalSize: "Total size:",
|
|
@@ -414,9 +414,9 @@ var DEFAULTS = {
|
|
|
414
414
|
expandDetails: "Expand details",
|
|
415
415
|
minimizeDetails: "Minimize details",
|
|
416
416
|
sessionNotQueued: (id, status) => `Upload session with ID ${id} is not queued, current status is ${status}`,
|
|
417
|
-
|
|
417
|
+
allCanceled: (count) => `All ${count} file(s) were canceled by user.`,
|
|
418
418
|
someUploaded: (count) => `${count} file(s) uploaded successfully!`,
|
|
419
|
-
|
|
419
|
+
someCanceled: (count) => `${count} file(s) canceled by user.`,
|
|
420
420
|
allUploaded: "Upload file(s) successfully!",
|
|
421
421
|
failedSummary: (failed, total) => `${failed}/${total} files failed to upload:`,
|
|
422
422
|
failReasonGeneric: "The file(s) may be too large or the connection may have been interrupted.",
|
|
@@ -9,14 +9,14 @@ interface StatusBadgeProps {
|
|
|
9
9
|
variant?: BadgeVariant;
|
|
10
10
|
/** Show animated pulse indicator */
|
|
11
11
|
pulse?: boolean;
|
|
12
|
-
/** Show a still indicator in the variant's
|
|
12
|
+
/** Show a still indicator in the variant's color */
|
|
13
13
|
dot?: boolean;
|
|
14
14
|
/** Size */
|
|
15
15
|
size?: 'sm' | 'md';
|
|
16
16
|
/** Additional class */
|
|
17
17
|
className?: string;
|
|
18
18
|
}
|
|
19
|
-
/** A line of text in a variant's
|
|
19
|
+
/** A line of text in a variant's color, for a status that stands beside a
|
|
20
20
|
* dot rather than inside a badge. */
|
|
21
21
|
export declare function statusTextClass(variant: BadgeVariant): string;
|
|
22
22
|
export declare function StatusBadge({ children, variant, pulse, dot, size, className, }: StatusBadgeProps): import("react").JSX.Element;
|
|
@@ -2,7 +2,7 @@ import { cx } from "../cx.js";
|
|
|
2
2
|
import { toneFill, toneSurface, toneText } from "./tone.js";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
//#region src/components/StatusBadge.tsx
|
|
5
|
-
/** A line of text in a variant's
|
|
5
|
+
/** A line of text in a variant's color, for a status that stands beside a
|
|
6
6
|
* dot rather than inside a badge. */
|
|
7
7
|
function statusTextClass(variant) {
|
|
8
8
|
return toneText[variant];
|
package/dist/components/Toast.js
CHANGED
|
@@ -135,7 +135,7 @@ function ToastViewport() {
|
|
|
135
135
|
return /* @__PURE__ */ jsx(Toast.Portal, { children: /* @__PURE__ */ jsx(Toast.Viewport, {
|
|
136
136
|
"data-toast-viewport": "",
|
|
137
137
|
"aria-label": common.notifications,
|
|
138
|
-
className: "fixed inset-
|
|
138
|
+
className: "fixed top-[calc(var(--app-inset-top,0px)+0.75rem)] right-0 left-(--app-inset-start,0px) z-(--layer-toast) mx-auto w-[calc(100vw-var(--app-inset-start,0px)-2rem)] outline-none sm:w-96",
|
|
139
139
|
children: /* @__PURE__ */ jsx(ToastList, {})
|
|
140
140
|
}) });
|
|
141
141
|
}
|
|
@@ -15,7 +15,7 @@ export interface ComboboxGroup<Value = string> {
|
|
|
15
15
|
/** Empty for a group that only reports a status; a search finds it by its heading. */
|
|
16
16
|
items: readonly ComboboxLeaf<Value>[];
|
|
17
17
|
}
|
|
18
|
-
/** One item: an option, or a bare string
|
|
18
|
+
/** One item: an option, or a bare string labeled by its value. */
|
|
19
19
|
export type ComboboxLeaf<Value = string> = ComboboxItem<Value> | (Value & string);
|
|
20
20
|
/** Items, groups under headings, or both: items between groups list where they sit, without a heading. */
|
|
21
21
|
export type ComboboxItems<Value = string> = readonly (ComboboxLeaf<Value> | ComboboxGroup<Value>)[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** How a status reads: its
|
|
1
|
+
/** How a status reads: its colors come from the theme's tone tokens, so every
|
|
2
2
|
* theme, dark ones included, sets them once. */
|
|
3
3
|
export type Tone = 'default' | 'success' | 'warning' | 'error' | 'info' | 'muted';
|
|
4
4
|
/** Text in the tone, for a status line beside a dot or icon. */
|
package/dist/editing.d.ts
CHANGED
|
@@ -320,7 +320,7 @@ export type SettingsYamlProblem = {
|
|
|
320
320
|
/** A key or list index on the way down to a YAML node. */
|
|
321
321
|
export type YamlSegment = string | number;
|
|
322
322
|
type UnknownRecord = Record<string, unknown>;
|
|
323
|
-
/** One input a built-in action takes in its step's `with`,
|
|
323
|
+
/** One input a built-in action takes in its step's `with`, labeled in the reader's language. */
|
|
324
324
|
export interface WorkflowActionInput {
|
|
325
325
|
key: string;
|
|
326
326
|
kind: 'text' | 'textarea' | 'number' | 'any' | 'list' | 'bool' | 'choice' | 'flags' | 'target';
|
|
@@ -107,7 +107,7 @@ function FileExplorerProvider({ children }) {
|
|
|
107
107
|
const fileProgress = session.progress.allFiles.get(relativePath);
|
|
108
108
|
if (fileProgress?.abortController && (fileProgress.status === "pending" || fileProgress.status === "uploading")) {
|
|
109
109
|
fileProgress.abortController.abort();
|
|
110
|
-
updateFileProgressInUploadSession(session, relativePath, { status: "
|
|
110
|
+
updateFileProgressInUploadSession(session, relativePath, { status: "canceled" });
|
|
111
111
|
}
|
|
112
112
|
};
|
|
113
113
|
const handleSessionCancel = (session) => {
|
|
@@ -116,12 +116,12 @@ function FileExplorerProvider({ children }) {
|
|
|
116
116
|
fileProgress.abortController.abort();
|
|
117
117
|
session.progress?.allFiles?.set(relativePath, {
|
|
118
118
|
...fileProgress,
|
|
119
|
-
status: "
|
|
119
|
+
status: "canceled"
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
122
|
});
|
|
123
123
|
session.abortController?.abort();
|
|
124
|
-
session.status = "
|
|
124
|
+
session.status = "canceled";
|
|
125
125
|
};
|
|
126
126
|
const startUploadSession = async (sessionId) => {
|
|
127
127
|
const session = getUploadSessionById(sessionId);
|
|
@@ -157,7 +157,7 @@ function FileExplorerProvider({ children }) {
|
|
|
157
157
|
const results = await mapWithConcurrency(session.uploadNodes, UPLOAD_CONCURRENCY, async (node) => {
|
|
158
158
|
const fileProgress = session.progress?.allFiles?.get(node.relativePath);
|
|
159
159
|
if (!fileProgress) return;
|
|
160
|
-
if (fileProgress.status === "
|
|
160
|
+
if (fileProgress.status === "canceled") throw new Error("Upload canceled by user");
|
|
161
161
|
updateFileProgressInUploadSession(session, node.relativePath, {
|
|
162
162
|
status: "uploading",
|
|
163
163
|
loadedBytes: 0,
|
|
@@ -172,11 +172,11 @@ function FileExplorerProvider({ children }) {
|
|
|
172
172
|
}
|
|
173
173
|
const input = uploadFileInput(session.storageName, objectPath, node.file);
|
|
174
174
|
const onProgress = (progress) => {
|
|
175
|
-
if (session.status === "
|
|
175
|
+
if (session.status === "canceled" || !session.progress?.allFiles) return;
|
|
176
176
|
const currentFileProgress = session.progress.allFiles.get(node.relativePath);
|
|
177
177
|
if (!currentFileProgress || [
|
|
178
178
|
"failed",
|
|
179
|
-
"
|
|
179
|
+
"canceled",
|
|
180
180
|
"completed"
|
|
181
181
|
].includes(currentFileProgress.status ?? "")) return;
|
|
182
182
|
updateFileProgressInUploadSession(session, node.relativePath, {
|
|
@@ -205,8 +205,8 @@ function FileExplorerProvider({ children }) {
|
|
|
205
205
|
updateFileProgressInUploadSession(session, node.relativePath, { status: "completed" });
|
|
206
206
|
} catch (error) {
|
|
207
207
|
const errorMessage = error instanceof Error ? error.message : String(error ?? "");
|
|
208
|
-
const
|
|
209
|
-
updateFileProgressInUploadSession(session, node.relativePath, { status:
|
|
208
|
+
const isCanceled = errorMessage.toLowerCase().includes("abort") || errorMessage.toLowerCase().includes("cancel");
|
|
209
|
+
updateFileProgressInUploadSession(session, node.relativePath, { status: isCanceled ? "canceled" : "failed" });
|
|
210
210
|
toast.update(sessionId, {
|
|
211
211
|
message: progressToast({
|
|
212
212
|
...session,
|
|
@@ -218,7 +218,7 @@ function FileExplorerProvider({ children }) {
|
|
|
218
218
|
}
|
|
219
219
|
}),
|
|
220
220
|
icon: false,
|
|
221
|
-
dismissible: session.status === "failed" || session.status === "
|
|
221
|
+
dismissible: session.status === "failed" || session.status === "canceled",
|
|
222
222
|
timeout: false
|
|
223
223
|
});
|
|
224
224
|
throw error;
|
|
@@ -267,12 +267,12 @@ function FileExplorerProvider({ children }) {
|
|
|
267
267
|
else if (abortedCount > 0 && successfulCount > 0) toast.update(sessionId, {
|
|
268
268
|
message: /* @__PURE__ */ jsxs("div", {
|
|
269
269
|
className: "w-full flex flex-col gap-1",
|
|
270
|
-
children: [/* @__PURE__ */ jsx("div", { children: t.upload.someUploaded(successfulCount) }), /* @__PURE__ */ jsx("div", { children: t.upload.
|
|
270
|
+
children: [/* @__PURE__ */ jsx("div", { children: t.upload.someUploaded(successfulCount) }), /* @__PURE__ */ jsx("div", { children: t.upload.someCanceled(abortedCount) })]
|
|
271
271
|
}),
|
|
272
272
|
tone: "success"
|
|
273
273
|
});
|
|
274
274
|
else if (abortedCount > 0 && successfulCount === 0) toast.update(sessionId, {
|
|
275
|
-
message: t.upload.
|
|
275
|
+
message: t.upload.allCanceled(abortedCount),
|
|
276
276
|
tone: "info"
|
|
277
277
|
});
|
|
278
278
|
else toast.update(sessionId, {
|
|
@@ -34,7 +34,7 @@ function useObjectBlobUrl(url, { maxBytes, knownSize, mimeType, cacheKey }) {
|
|
|
34
34
|
});
|
|
35
35
|
return;
|
|
36
36
|
}
|
|
37
|
-
let
|
|
37
|
+
let canceled = false;
|
|
38
38
|
const record = loadPreviewContent(cacheKey, async (signal) => {
|
|
39
39
|
const response = await fetchPreviewObject(cacheKey, url, signal);
|
|
40
40
|
const { bytes, tooLarge: exceeded } = await readCappedBytes(response, maxBytes);
|
|
@@ -52,17 +52,17 @@ function useObjectBlobUrl(url, { maxBytes, knownSize, mimeType, cacheKey }) {
|
|
|
52
52
|
loading: true
|
|
53
53
|
});
|
|
54
54
|
record.promise.then((result) => {
|
|
55
|
-
if (!
|
|
55
|
+
if (!canceled) setState(toState(result));
|
|
56
56
|
}).catch((err) => {
|
|
57
57
|
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
58
|
-
if (!
|
|
58
|
+
if (!canceled) setState({
|
|
59
59
|
...IDLE,
|
|
60
60
|
error: err instanceof Error ? err.message : "Failed to load file",
|
|
61
61
|
corsError: err instanceof CorsError
|
|
62
62
|
});
|
|
63
63
|
});
|
|
64
64
|
return () => {
|
|
65
|
-
|
|
65
|
+
canceled = true;
|
|
66
66
|
};
|
|
67
67
|
}, [
|
|
68
68
|
url,
|
|
@@ -60,7 +60,7 @@ function useObjectText(url, { maxBytes, maxLines, knownSize, cacheKey, tail = fa
|
|
|
60
60
|
});
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
|
-
let
|
|
63
|
+
let canceled = false;
|
|
64
64
|
const record = loadPreviewContent(key, (signal) => readObjectText(signal, {
|
|
65
65
|
url,
|
|
66
66
|
cacheKey,
|
|
@@ -74,17 +74,17 @@ function useObjectText(url, { maxBytes, maxLines, knownSize, cacheKey, tail = fa
|
|
|
74
74
|
loading: true
|
|
75
75
|
});
|
|
76
76
|
record.promise.then((result) => {
|
|
77
|
-
if (!
|
|
77
|
+
if (!canceled) setState(toState(result));
|
|
78
78
|
}).catch((err) => {
|
|
79
79
|
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
80
|
-
if (!
|
|
80
|
+
if (!canceled) setState({
|
|
81
81
|
...IDLE,
|
|
82
82
|
error: err instanceof Error ? err.message : "Failed to load file",
|
|
83
83
|
corsError: err instanceof CorsError
|
|
84
84
|
});
|
|
85
85
|
});
|
|
86
86
|
return () => {
|
|
87
|
-
|
|
87
|
+
canceled = true;
|
|
88
88
|
};
|
|
89
89
|
}, [
|
|
90
90
|
url,
|
|
@@ -46,7 +46,7 @@ function usePresignedUrl(node, getPresignedUrl, { enabled, linkErrorMessage }) {
|
|
|
46
46
|
});
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
|
-
let
|
|
49
|
+
let canceled = false;
|
|
50
50
|
const record = loadPreviewUrl(path, () => getUrlRef.current(node));
|
|
51
51
|
setState(record.value ? toState(record.value, linkErrorRef.current) : {
|
|
52
52
|
url: null,
|
|
@@ -54,16 +54,16 @@ function usePresignedUrl(node, getPresignedUrl, { enabled, linkErrorMessage }) {
|
|
|
54
54
|
error: null
|
|
55
55
|
});
|
|
56
56
|
record.promise.then((result) => {
|
|
57
|
-
if (!
|
|
57
|
+
if (!canceled) setState(toState(result, linkErrorRef.current));
|
|
58
58
|
}).catch((err) => {
|
|
59
|
-
if (!
|
|
59
|
+
if (!canceled) setState({
|
|
60
60
|
url: null,
|
|
61
61
|
loading: false,
|
|
62
62
|
error: err instanceof Error ? err.message : linkErrorRef.current
|
|
63
63
|
});
|
|
64
64
|
});
|
|
65
65
|
return () => {
|
|
66
|
-
|
|
66
|
+
canceled = true;
|
|
67
67
|
};
|
|
68
68
|
}, [path]);
|
|
69
69
|
const cached = path ? peekPreviewUrl(path) : void 0;
|
|
@@ -79,14 +79,14 @@ export interface UploadFileProgress {
|
|
|
79
79
|
totalBytes?: number;
|
|
80
80
|
percentage?: number;
|
|
81
81
|
abortController?: AbortController;
|
|
82
|
-
status?: 'pending' | 'uploading' | 'completed' | 'failed' | '
|
|
82
|
+
status?: 'pending' | 'uploading' | 'completed' | 'failed' | 'canceled';
|
|
83
83
|
}
|
|
84
84
|
export interface UploadSession {
|
|
85
85
|
id: string;
|
|
86
86
|
storageName: string;
|
|
87
87
|
targetPath: string;
|
|
88
88
|
uploadNodes: UploadNode[];
|
|
89
|
-
status: 'queued' | 'uploading' | 'completed' | 'failed' | '
|
|
89
|
+
status: 'queued' | 'uploading' | 'completed' | 'failed' | 'canceled';
|
|
90
90
|
queueIndex: number;
|
|
91
91
|
client: IFileExplorerClient;
|
|
92
92
|
provider: IFileExplorerProvider;
|
|
@@ -42,9 +42,9 @@ function getSessionStatusInfo(session, queueLength, strings) {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
-
function barTone({ isCompleted,
|
|
45
|
+
function barTone({ isCompleted, isCanceled, isFailed }) {
|
|
46
46
|
if (isCompleted) return "success";
|
|
47
|
-
if (
|
|
47
|
+
if (isCanceled) return "muted";
|
|
48
48
|
return isFailed ? "error" : "accent";
|
|
49
49
|
}
|
|
50
50
|
function FileProgressBar({ fileProgress, fileName, onCancelFile }) {
|
|
@@ -55,9 +55,9 @@ function FileProgressBar({ fileProgress, fileName, onCancelFile }) {
|
|
|
55
55
|
const loadedBytes = fileProgress.loadedBytes || 0;
|
|
56
56
|
const totalBytes = fileProgress.fileSizeBytes || fileProgress.totalBytes || 0;
|
|
57
57
|
const isCompleted = percentage === 100 || fileProgress.status === "completed";
|
|
58
|
-
const
|
|
58
|
+
const isCanceled = fileProgress.status === "canceled";
|
|
59
59
|
const isFailed = fileProgress.status === "failed";
|
|
60
|
-
const isSettled = isCompleted ||
|
|
60
|
+
const isSettled = isCompleted || isCanceled || isFailed;
|
|
61
61
|
const canCancel = !!fileProgress.abortController && !isSettled;
|
|
62
62
|
const stop = (e, then) => {
|
|
63
63
|
e.stopPropagation();
|
|
@@ -71,11 +71,11 @@ function FileProgressBar({ fileProgress, fileName, onCancelFile }) {
|
|
|
71
71
|
value: percentage,
|
|
72
72
|
tone: barTone({
|
|
73
73
|
isCompleted,
|
|
74
|
-
|
|
74
|
+
isCanceled,
|
|
75
75
|
isFailed
|
|
76
76
|
}),
|
|
77
77
|
label: fileName,
|
|
78
|
-
status:
|
|
78
|
+
status: isCanceled ? strings.canceled : isCompleted ? strings.completed : isFailed ? strings.failed : void 0,
|
|
79
79
|
detail: !isSettled && totalBytes > 0 ? `${formatBytes(loadedBytes)} / ${formatBytes(totalBytes)}` : void 0
|
|
80
80
|
}),
|
|
81
81
|
!showCancelConfirm && canCancel && /* @__PURE__ */ jsx(IconButton, {
|
|
@@ -120,14 +120,14 @@ function UploadSessionToast({ session, queueLength, onCancel, onCancelFile }) {
|
|
|
120
120
|
const statusInfo = getSessionStatusInfo(session, queueLength, strings);
|
|
121
121
|
const targetPath = `${session.targetPath}/`;
|
|
122
122
|
const isCompleted = session.status === "completed";
|
|
123
|
-
const
|
|
123
|
+
const isCanceled = session.status === "canceled";
|
|
124
124
|
const isFailed = session.status === "failed";
|
|
125
|
-
const isUploading = !isCompleted && !
|
|
125
|
+
const isUploading = !isCompleted && !isCanceled && !isFailed;
|
|
126
126
|
const totalProgress = session.progress?.overallProgress || 0;
|
|
127
127
|
const totalSize = session.progress?.totalSizeBytes || 0;
|
|
128
128
|
const sessionTone = barTone({
|
|
129
129
|
isCompleted,
|
|
130
|
-
|
|
130
|
+
isCanceled,
|
|
131
131
|
isFailed
|
|
132
132
|
});
|
|
133
133
|
const allFiles = session.progress?.allFiles ? Array.from(session.progress.allFiles.values()) : [];
|
|
@@ -17,7 +17,7 @@ export interface FieldOptionGroup<Value = string> {
|
|
|
17
17
|
category?: string | undefined;
|
|
18
18
|
options: readonly (FieldOptionItem<Value> | string)[];
|
|
19
19
|
}
|
|
20
|
-
/** A dropdown option: a bare string is a choice
|
|
20
|
+
/** A dropdown option: a bare string is a choice labeled by its value. */
|
|
21
21
|
export type FieldOption<Value = string> = string | FieldOptionItem<Value> | FieldOptionGroup<Value>;
|
|
22
22
|
/** A picker item that also sets other fields when chosen, through `secondaryField`. */
|
|
23
23
|
export type ChoiceItem<Value = unknown> = ComboboxItem<Value> & {
|
|
@@ -4,7 +4,7 @@ export interface Suggestion {
|
|
|
4
4
|
value: string;
|
|
5
5
|
label?: string | undefined;
|
|
6
6
|
}
|
|
7
|
-
/** A
|
|
7
|
+
/** A labeled suggestion shows its meaning, with the value it writes beneath. */
|
|
8
8
|
export declare function suggestionItems(suggestions: Suggestion[]): ComboboxItem[];
|
|
9
9
|
/** A field that takes any typed value, offering `suggestions` as it's typed. */
|
|
10
10
|
export declare function SuggestionInput({ suggestions, ...props }: Omit<AutocompleteProps, 'items' | 'defaultValue'> & {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Autocomplete } from "../components/Combobox.js";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
//#region src/graph/SuggestionInput.tsx
|
|
4
|
-
/** A
|
|
4
|
+
/** A labeled suggestion shows its meaning, with the value it writes beneath. */
|
|
5
5
|
function suggestionItems(suggestions) {
|
|
6
6
|
return suggestions.map(({ value, label }) => label ? {
|
|
7
7
|
value,
|
|
@@ -33,7 +33,7 @@ interface DragOptions {
|
|
|
33
33
|
onMove?: (client: Point) => void;
|
|
34
34
|
/** The release; `dragged` tells a drag from a click. */
|
|
35
35
|
onEnd?: (client: Point, dragged: boolean) => void;
|
|
36
|
-
/** A
|
|
36
|
+
/** A canceled pointer, or Escape when `escape` is on. */
|
|
37
37
|
onCancel?: (dragged: boolean) => void;
|
|
38
38
|
/** Escape cancels the press, and goes no further. */
|
|
39
39
|
escape?: boolean;
|
|
@@ -42,7 +42,7 @@ interface DragOptions {
|
|
|
42
42
|
/** The click after the release: swallowed always, only after a drag, or never. */
|
|
43
43
|
swallowClick?: 'always' | 'drag' | 'never';
|
|
44
44
|
}
|
|
45
|
-
/** Follows a press from `start` until it's released or
|
|
45
|
+
/** Follows a press from `start` until it's released or canceled. */
|
|
46
46
|
export declare function trackDrag(start: Point, { onMove, onEnd, onCancel, escape: cancelOnEscape, holdText, swallowClick, }: DragOptions): void;
|
|
47
47
|
export declare function place(box: Box): {
|
|
48
48
|
left: number;
|
|
@@ -69,7 +69,7 @@ function suppressNextClick() {
|
|
|
69
69
|
window.addEventListener("click", swallow, true);
|
|
70
70
|
setTimeout(() => window.removeEventListener("click", swallow, true), 0);
|
|
71
71
|
}
|
|
72
|
-
/** Follows a press from `start` until it's released or
|
|
72
|
+
/** Follows a press from `start` until it's released or canceled. */
|
|
73
73
|
function trackDrag(start, { onMove, onEnd, onCancel, escape: cancelOnEscape = false, holdText, swallowClick = "never" }) {
|
|
74
74
|
let dragged = false;
|
|
75
75
|
const cleanup = () => {
|
|
@@ -203,14 +203,14 @@ export declare const GRAPH_EDITOR_STRINGS: {
|
|
|
203
203
|
default: string;
|
|
204
204
|
always: string;
|
|
205
205
|
failure: string;
|
|
206
|
-
|
|
206
|
+
canceled: string;
|
|
207
207
|
success: string;
|
|
208
208
|
};
|
|
209
209
|
jobConditions: {
|
|
210
210
|
default: string;
|
|
211
211
|
always: string;
|
|
212
212
|
failure: string;
|
|
213
|
-
|
|
213
|
+
canceled: string;
|
|
214
214
|
success: string;
|
|
215
215
|
};
|
|
216
216
|
modeFromInput: string;
|
|
@@ -204,14 +204,14 @@ var GRAPH_EDITOR_STRINGS = {
|
|
|
204
204
|
default: "After the steps before it succeed",
|
|
205
205
|
always: "Always, even after a failure or a cancel",
|
|
206
206
|
failure: "Only after a step failed",
|
|
207
|
-
|
|
207
|
+
canceled: "Only after the run was canceled",
|
|
208
208
|
success: "Only if nothing failed or was canceled"
|
|
209
209
|
},
|
|
210
210
|
jobConditions: {
|
|
211
211
|
default: "After the jobs it needs succeed",
|
|
212
212
|
always: "Always, whatever the jobs it needs did",
|
|
213
213
|
failure: "Only if a job it needs failed",
|
|
214
|
-
|
|
214
|
+
canceled: "Only if a job it needs was canceled",
|
|
215
215
|
success: "Only if every job it needs succeeded"
|
|
216
216
|
},
|
|
217
217
|
modeFromInput: "From an input",
|
|
@@ -620,8 +620,8 @@ var INPUTS_EDITOR_STRINGS = {
|
|
|
620
620
|
hidden: "Hide the field from the form; its default is still used. Use an expression to decide when.",
|
|
621
621
|
disabled: "Show the field but don’t let users change it. Use an expression to decide when.",
|
|
622
622
|
ignore: "Don’t pass this field’s value to the run. Use an expression to decide when.",
|
|
623
|
-
placeholder: "
|
|
624
|
-
prefillDefault: "Put the default in the field as its value. When off, the field starts empty and shows the default as
|
|
623
|
+
placeholder: "Gray hint text shown while the field is empty.",
|
|
624
|
+
prefillDefault: "Put the default in the field as its value. When off, the field starts empty and shows the default as gray hint text.",
|
|
625
625
|
textarea: "Show a bigger box for several lines of text.",
|
|
626
626
|
minLength: "The fewest characters the value can have.",
|
|
627
627
|
maxLength: "The most characters the value can have.",
|
package/dist/graph/inputRefs.js
CHANGED
|
@@ -182,7 +182,7 @@ function FlagField({ label, yamlKey, description, value, original, fallback = fa
|
|
|
182
182
|
var CONDITIONS = [
|
|
183
183
|
["always", "always"],
|
|
184
184
|
["failure()", "failure"],
|
|
185
|
-
["
|
|
185
|
+
["canceled()", "canceled"],
|
|
186
186
|
["success()", "success"]
|
|
187
187
|
];
|
|
188
188
|
/** When a job or step runs: any condition, typed, with the executor's status checks and the form's switches offered. */
|
package/dist/icons.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Activity, Archive, ArrowDown, ArrowDownWideNarrow, ArrowLeft, ArrowRigh
|
|
|
3
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
//#region src/icons.tsx
|
|
5
5
|
var STROKE_WIDTH = 2;
|
|
6
|
-
/** The text's size and
|
|
6
|
+
/** The text's size and color by default, so a class such as `size-4` or a
|
|
7
7
|
* parent's font size sets it. */
|
|
8
8
|
function icon(Glyph, defaults) {
|
|
9
9
|
function Icon({ title, children, ...props }) {
|