@tangle-network/agent-app 0.46.1 → 0.46.3
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 +7 -7
- package/dist/assistant/index.js +2 -1
- package/dist/assistant/index.js.map +1 -1
- package/dist/chunk-KKBTPZIE.js +805 -0
- package/dist/chunk-KKBTPZIE.js.map +1 -0
- package/dist/{chunk-2RTYQU4W.js → chunk-LEGZX5MQ.js} +150 -4
- package/dist/chunk-LEGZX5MQ.js.map +1 -0
- package/dist/{chunk-MBKNKAN2.js → chunk-RZ6MYWMJ.js} +416 -1210
- package/dist/chunk-RZ6MYWMJ.js.map +1 -0
- package/dist/stories/studio/StudioProviders.d.ts +10 -0
- package/dist/stories/studio/fixtures.d.ts +10 -2
- package/dist/studio/audio-preview.d.ts +12 -0
- package/dist/studio/generation.d.ts +22 -1
- package/dist/studio/index.d.ts +2 -0
- package/dist/studio/index.js +29 -1
- package/dist/studio/ports.d.ts +54 -0
- package/dist/studio-react/composer-option-controls.d.ts +19 -8
- package/dist/studio-react/download-generations.d.ts +4 -0
- package/dist/studio-react/index.d.ts +38 -22
- package/dist/studio-react/index.js +2198 -545
- package/dist/studio-react/index.js.map +1 -1
- package/dist/studio-react/media-tile.d.ts +21 -0
- package/dist/studio-react/media-viewer.d.ts +12 -0
- package/dist/studio-react/studio-confirm.d.ts +8 -0
- package/dist/studio-react/studio-generation-screen.d.ts +42 -0
- package/dist/studio-react/studio-history-screen.d.ts +28 -0
- package/dist/studio-react/studio-home-screen.d.ts +20 -0
- package/dist/studio-react/studio-playback.d.ts +31 -0
- package/dist/studio-react/studio-toasts.d.ts +21 -0
- package/dist/studio-react/studio.css +142 -72
- package/dist/studio-react/use-batch-navigation.d.ts +20 -0
- package/dist/studio-react/use-deferred-delete.d.ts +19 -0
- package/dist/studio-react/use-generation-history.d.ts +22 -0
- package/dist/studio-react/vault-path-popover.d.ts +12 -0
- package/dist/web-react/index.js +8 -6
- package/package.json +11 -16
- package/dist/chunk-2RTYQU4W.js.map +0 -1
- package/dist/chunk-MBKNKAN2.js.map +0 -1
- package/dist/studio-react/generation-card.d.ts +0 -9
- package/dist/studio-react/generation-detail-modal.d.ts +0 -11
- package/dist/studio-react/generation-detail.d.ts +0 -6
- package/dist/studio-react/generation-grid.d.ts +0 -15
- package/dist/studio-react/library-drawer.d.ts +0 -12
- package/dist/studio-react/library-panel.d.ts +0 -15
- package/dist/studio-react/result-canvas.d.ts +0 -6
- package/dist/studio-react/studio-header.d.ts +0 -5
- package/dist/studio-react/studio-sheet.d.ts +0 -12
- package/dist/studio-react/studio-workspace.d.ts +0 -21
- package/dist/studio-react/type-config.d.ts +0 -11
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CSSProperties, type JSX } from 'react';
|
|
2
|
+
import { type Generation } from '../studio/generation';
|
|
3
|
+
import type { StudioMediaActions, VaultSaveResult } from '../studio/ports';
|
|
4
|
+
export interface MediaTileProps {
|
|
5
|
+
generation: Generation;
|
|
6
|
+
context: 'home' | 'generation' | 'history';
|
|
7
|
+
onOpen: (generation: Generation) => void;
|
|
8
|
+
actions?: StudioMediaActions;
|
|
9
|
+
/** Generation screen passes the batch aspect; omitted → square. */
|
|
10
|
+
aspectRatio?: number;
|
|
11
|
+
/** 26 for grids (default), 72 for wide generation/viewer tiles. */
|
|
12
|
+
waveformBars?: number;
|
|
13
|
+
selectMode?: boolean;
|
|
14
|
+
selected?: boolean;
|
|
15
|
+
onToggleSelect?: (id: string) => void;
|
|
16
|
+
onRequestDelete?: (generation: Generation) => void;
|
|
17
|
+
onSaved?: (results: readonly VaultSaveResult[]) => void;
|
|
18
|
+
className?: string;
|
|
19
|
+
style?: CSSProperties;
|
|
20
|
+
}
|
|
21
|
+
export declare function MediaTile({ generation, context, onOpen, actions, aspectRatio, waveformBars, selectMode, selected, onToggleSelect, onRequestDelete, onSaved, className, style, }: MediaTileProps): JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type JSX } from 'react';
|
|
2
|
+
import { type Generation, type StudioMediaActions, type VaultSaveResult } from '../studio';
|
|
3
|
+
export interface MediaViewerModalProps {
|
|
4
|
+
generation: Generation | null;
|
|
5
|
+
onClose: () => void;
|
|
6
|
+
actions?: StudioMediaActions;
|
|
7
|
+
/** Screens own the delete confirm; absent hides the Delete button. */
|
|
8
|
+
onRequestDelete?: (generation: Generation) => void;
|
|
9
|
+
/** After a successful save from the footer popover. */
|
|
10
|
+
onSaved?: (results: readonly VaultSaveResult[]) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function MediaViewerModal({ generation, onClose, actions, onRequestDelete, onSaved, }: MediaViewerModalProps): JSX.Element | null;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type JSX } from 'react';
|
|
2
|
+
export interface StudioConfirmDialogProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
count: number;
|
|
5
|
+
onConfirm: () => void;
|
|
6
|
+
onCancel: () => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function StudioConfirmDialog({ open, count, onConfirm, onCancel, }: StudioConfirmDialogProps): JSX.Element | null;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The generation screen: ONE batch's results, with the composer docked under
|
|
3
|
+
* them.
|
|
4
|
+
*
|
|
5
|
+
* The screen is a flex column that fills its host — results scroll, the dock
|
|
6
|
+
* does not. The dock band is deliberately OPAQUE (`bg-background`): it sits
|
|
7
|
+
* above the scrolling results and a translucent band would let a tile read
|
|
8
|
+
* through the composer's own card. The 28px fade above it comes from
|
|
9
|
+
* `.studio-dock::before` in `./studio.css`.
|
|
10
|
+
*
|
|
11
|
+
* The dock's measured height is published two ways, because two different
|
|
12
|
+
* consumers need it: as the `--studio-dock-h` custom property on the screen
|
|
13
|
+
* root (host CSS, and anything nested that must clear the band) and through
|
|
14
|
+
* `useStudioToast().setDockLift`, which lifts the toast stack off the composer.
|
|
15
|
+
* The body's own bottom padding is applied from the measured value directly
|
|
16
|
+
* rather than read back through that property — a CSS custom property a
|
|
17
|
+
* component READS must be one `tokens.css` defines (the theme contract), and a
|
|
18
|
+
* dock height is a layout measurement, not a theme token.
|
|
19
|
+
*
|
|
20
|
+
* A prompt sent from the DOCK starts a NEW batch, which is a different screen.
|
|
21
|
+
* This one reports it (`onOpenGeneration`) exactly once per new batch key and
|
|
22
|
+
* lets the host navigate; every row of that batch still flows through
|
|
23
|
+
* `onGenerated` so the host's list stays whole.
|
|
24
|
+
*
|
|
25
|
+
* Assumes a `StudioToastProvider` and a `StudioPlaybackProvider` above it.
|
|
26
|
+
*/
|
|
27
|
+
import { type JSX } from 'react';
|
|
28
|
+
import { type Generation, type StudioMediaActions } from '../studio';
|
|
29
|
+
export interface StudioGenerationScreenProps {
|
|
30
|
+
/** Full merged list from the host (`useStudioGenerations`). */
|
|
31
|
+
generations: Generation[];
|
|
32
|
+
batchKey: string;
|
|
33
|
+
onGenerated: (generation: Generation) => void;
|
|
34
|
+
/** A dock submit that starts a NEW batch navigates via the host. */
|
|
35
|
+
onOpenGeneration: (batchKey: string, first: Generation) => void;
|
|
36
|
+
onBack: () => void;
|
|
37
|
+
workspaceId?: string;
|
|
38
|
+
pickReferenceImage?: () => Promise<string | null>;
|
|
39
|
+
actions?: StudioMediaActions;
|
|
40
|
+
className?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function StudioGenerationScreen({ generations, batchKey, onGenerated, onOpenGeneration, onBack, workspaceId, pickReferenceImage, actions, className, }: StudioGenerationScreenProps): JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The studio history screen — the full media library behind the home screen's
|
|
3
|
+
* "View history" button: search, a media-type filter, cursor-paged infinite
|
|
4
|
+
* scroll, and the multi-select bar that runs download / save-to-vault / delete
|
|
5
|
+
* over a whole selection at once.
|
|
6
|
+
*
|
|
7
|
+
* It ASSUMES a `StudioToastProvider` and a `StudioPlaybackProvider` above it —
|
|
8
|
+
* the deferred delete it drives lives in both (undo toast, stopping audio for a
|
|
9
|
+
* row that is going away), and every screen in this surface makes the same
|
|
10
|
+
* assumption so a route layout can mount the two providers once.
|
|
11
|
+
*
|
|
12
|
+
* The screen never fetches: `fetchPage` is the product's own paged endpoint and
|
|
13
|
+
* `actions` are its media seams. An ABSENT action hides its control rather than
|
|
14
|
+
* rendering a batch button that does nothing.
|
|
15
|
+
*/
|
|
16
|
+
import { type JSX } from 'react';
|
|
17
|
+
import { type FetchGenerationsPage, type GenerationPage, type StudioMediaActions } from '../studio/ports';
|
|
18
|
+
export interface StudioHistoryScreenProps {
|
|
19
|
+
fetchPage: FetchGenerationsPage;
|
|
20
|
+
/** SSR/loader page 1 for the DEFAULT view (no search, no type filter). */
|
|
21
|
+
initialPage?: GenerationPage;
|
|
22
|
+
onBack: () => void;
|
|
23
|
+
actions?: StudioMediaActions;
|
|
24
|
+
/** Trailing debounce before a keystroke becomes a fetch. */
|
|
25
|
+
searchDebounceMs?: number;
|
|
26
|
+
className?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function StudioHistoryScreen({ fetchPage, initialPage, onBack, actions, searchDebounceMs, className, }: StudioHistoryScreenProps): JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type JSX } from 'react';
|
|
2
|
+
import type { Generation } from '../studio/generation';
|
|
3
|
+
import type { StudioMediaActions } from '../studio/ports';
|
|
4
|
+
export interface StudioHomeScreenProps {
|
|
5
|
+
/** Newest first, from the host loader (host runs useStudioGenerations). */
|
|
6
|
+
generations: Generation[];
|
|
7
|
+
onGenerated: (generation: Generation) => void;
|
|
8
|
+
onOpenGeneration: (batchKey: string, first: Generation) => void;
|
|
9
|
+
onOpenHistory: () => void;
|
|
10
|
+
workspaceId?: string;
|
|
11
|
+
pickReferenceImage?: () => Promise<string | null>;
|
|
12
|
+
actions?: StudioMediaActions;
|
|
13
|
+
recentLimit?: number;
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Studio's create-and-recent-media landing screen. The host must render this
|
|
18
|
+
* inside both `StudioToastProvider` and `StudioPlaybackProvider`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function StudioHomeScreen({ generations, onGenerated, onOpenGeneration, onOpenHistory, workspaceId, pickReferenceImage, actions, recentLimit, className, }: StudioHomeScreenProps): JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type JSX, type ReactNode } from 'react';
|
|
2
|
+
import type { Generation } from '../studio/generation';
|
|
3
|
+
export interface StudioAudioElementLike {
|
|
4
|
+
src: string;
|
|
5
|
+
currentTime: number;
|
|
6
|
+
readonly duration: number;
|
|
7
|
+
play(): Promise<void> | void;
|
|
8
|
+
pause(): void;
|
|
9
|
+
addEventListener(type: string, listener: () => void): void;
|
|
10
|
+
removeEventListener(type: string, listener: () => void): void;
|
|
11
|
+
}
|
|
12
|
+
export interface StudioPlayback {
|
|
13
|
+
activeId: string | null;
|
|
14
|
+
playing: boolean;
|
|
15
|
+
durationSeconds: number;
|
|
16
|
+
play(generation: Generation): void;
|
|
17
|
+
pause(): void;
|
|
18
|
+
toggle(generation: Generation): void;
|
|
19
|
+
stop(): void;
|
|
20
|
+
seekTo(generation: Generation, seconds: number): void;
|
|
21
|
+
seekBy(seconds: number): void;
|
|
22
|
+
getPositionSeconds(): number;
|
|
23
|
+
registerPositionNode(id: string, node: HTMLElement | null): () => void;
|
|
24
|
+
registerTimeNode(node: HTMLElement | null): () => void;
|
|
25
|
+
}
|
|
26
|
+
export declare function formatClock(seconds: number): string;
|
|
27
|
+
export declare function StudioPlaybackProvider(props: {
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
createAudioElement?: () => StudioAudioElementLike;
|
|
30
|
+
}): JSX.Element;
|
|
31
|
+
export declare function useStudioPlayback(): StudioPlayback;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type JSX, type ReactNode } from 'react';
|
|
2
|
+
export interface StudioToastInput {
|
|
3
|
+
message: string;
|
|
4
|
+
action?: {
|
|
5
|
+
label: string;
|
|
6
|
+
run: () => void;
|
|
7
|
+
};
|
|
8
|
+
durationMs?: number;
|
|
9
|
+
/** Fires exactly once when the toast leaves for ANY reason. */
|
|
10
|
+
onDismiss?: (reason: 'timeout' | 'dismissed' | 'action') => void;
|
|
11
|
+
}
|
|
12
|
+
interface StudioToastContextValue {
|
|
13
|
+
toast: (input: StudioToastInput) => string;
|
|
14
|
+
dismiss: (id: string) => void;
|
|
15
|
+
setDockLift: (px: number | null) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function StudioToastProvider({ children }: {
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
}): JSX.Element;
|
|
20
|
+
export declare function useStudioToast(): StudioToastContextValue;
|
|
21
|
+
export {};
|
|
@@ -1,66 +1,3 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Studio animations: the right-side library sheet slide/fade, the result-canvas
|
|
3
|
-
* bloom-in, and the neutral skeleton shimmer. Ships as a raw stylesheet (the
|
|
4
|
-
* `./styles` precedent) so apps that don't already define these classes can
|
|
5
|
-
* `import '@tangle-network/agent-app/studio-react/styles'` once. Token-driven
|
|
6
|
-
* (`--muted`, `--muted-foreground`) so it inherits the consuming app's theme.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/* ── Studio: library sheet + result canvas ── */
|
|
10
|
-
@keyframes studio-sheet-in {
|
|
11
|
-
from { transform: translateX(100%); }
|
|
12
|
-
to { transform: translateX(0); }
|
|
13
|
-
}
|
|
14
|
-
@keyframes studio-sheet-out {
|
|
15
|
-
from { transform: translateX(0); }
|
|
16
|
-
to { transform: translateX(100%); }
|
|
17
|
-
}
|
|
18
|
-
@keyframes studio-overlay-fade {
|
|
19
|
-
from { opacity: 0; }
|
|
20
|
-
to { opacity: 1; }
|
|
21
|
-
}
|
|
22
|
-
@keyframes studio-bloom {
|
|
23
|
-
from { opacity: 0; transform: translateY(6px) scale(0.99); }
|
|
24
|
-
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/* Dedicated classes (not tailwindcss-animate .animate-in) so the right-side
|
|
28
|
-
sheet is unaffected by a conflicting .animate-in rule. Driven by Radix's
|
|
29
|
-
data-state on the dialog parts. */
|
|
30
|
-
.studio-sheet-content[data-state='open'] { animation: studio-sheet-in 0.28s cubic-bezier(0.32, 0.72, 0, 1); }
|
|
31
|
-
.studio-sheet-content[data-state='closed'] { animation: studio-sheet-out 0.2s cubic-bezier(0.4, 0, 1, 1); }
|
|
32
|
-
.studio-sheet-overlay[data-state='open'] { animation: studio-overlay-fade 0.28s ease-out; }
|
|
33
|
-
.studio-sheet-overlay[data-state='closed'] { animation: studio-overlay-fade 0.2s ease-in reverse; }
|
|
34
|
-
.studio-bloom { animation: studio-bloom 0.3s cubic-bezier(0.22, 1, 0.36, 1); }
|
|
35
|
-
|
|
36
|
-
/* Neutral skeleton shimmer — a soft grey band sweeps the muted surface.
|
|
37
|
-
`.shimmer` is an alias so the generation card and result canvas share one
|
|
38
|
-
skeleton treatment. */
|
|
39
|
-
@keyframes studio-shimmer {
|
|
40
|
-
0% { background-position: 200% 0; }
|
|
41
|
-
100% { background-position: -200% 0; }
|
|
42
|
-
}
|
|
43
|
-
.studio-shimmer,
|
|
44
|
-
.shimmer {
|
|
45
|
-
background-color: hsl(var(--muted));
|
|
46
|
-
background-image: linear-gradient(
|
|
47
|
-
90deg,
|
|
48
|
-
transparent 0%,
|
|
49
|
-
hsl(var(--muted-foreground) / 0.22) 50%,
|
|
50
|
-
transparent 100%
|
|
51
|
-
);
|
|
52
|
-
background-size: 200% 100%;
|
|
53
|
-
background-repeat: no-repeat;
|
|
54
|
-
animation: studio-shimmer 1.7s ease-in-out infinite;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/* Library card entrance. */
|
|
58
|
-
@keyframes studio-row-in {
|
|
59
|
-
from { opacity: 0; transform: translateY(4px); }
|
|
60
|
-
to { opacity: 1; transform: translateY(0); }
|
|
61
|
-
}
|
|
62
|
-
.animate-row-in { animation: studio-row-in 0.25s ease-out both; }
|
|
63
|
-
|
|
64
1
|
/* ── Studio composer: the option band ──
|
|
65
2
|
The control row's middle section scrolls horizontally and must not show a
|
|
66
3
|
scrollbar (it sits 34px tall inside a chat card, where a bar eats a third of
|
|
@@ -112,13 +49,146 @@
|
|
|
112
49
|
.studio-send { background-image: var(--studio-send-gradient); }
|
|
113
50
|
|
|
114
51
|
@media (prefers-reduced-motion: reduce) {
|
|
115
|
-
.studio-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
52
|
+
.studio-pill-in { animation: none; }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* ── Layer ladder ─────────────────────────────────────────────────────
|
|
56
|
+
PopoverSurface hardcodes z-[1000] (src/web-react/controls.tsx) and a
|
|
57
|
+
className cannot override it (equal specificity → stylesheet order wins).
|
|
58
|
+
The ladder is built AROUND 1000: the viewer sits below it so the
|
|
59
|
+
save-to-vault popover is clickable from inside the viewer; the delete
|
|
60
|
+
confirm sits above the popover; toasts above everything.
|
|
61
|
+
Pinned by tests/studio/layer-order.test.ts. */
|
|
62
|
+
.studio-layer-viewer { z-index: 900; }
|
|
63
|
+
.studio-layer-confirm { z-index: 1100; }
|
|
64
|
+
.studio-layer-toasts { z-index: 1200; }
|
|
65
|
+
|
|
66
|
+
/* ── Grids ── 3px gaps, square corners, no tile border: at a 3px gap a 1px
|
|
67
|
+
border reads as a hairline seam and the grid stops reading as one sheet. */
|
|
68
|
+
.studio-grid { display: grid; gap: 3px; }
|
|
69
|
+
.studio-grid-library { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }
|
|
70
|
+
@media (max-width: 640px) {
|
|
71
|
+
.studio-grid-library { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* ── Tile ── the scrim/chip/tooltip colors are theme-INVARIANT literals:
|
|
75
|
+
they sit on generated media and stay dark in both themes. */
|
|
76
|
+
.studio-tile { position: relative; isolation: isolate; border-radius: 0; }
|
|
77
|
+
.studio-tile-scrim {
|
|
78
|
+
background: linear-gradient(to top,
|
|
79
|
+
rgba(10,10,20,.74) 0%, rgba(10,10,20,.34) 46%,
|
|
80
|
+
rgba(10,10,20,.30) 66%, rgba(10,10,20,.62) 100%);
|
|
81
|
+
opacity: 0; transition: opacity .18s ease; pointer-events: none;
|
|
82
|
+
}
|
|
83
|
+
.studio-tile:hover .studio-tile-scrim,
|
|
84
|
+
.studio-tile:focus-within .studio-tile-scrim,
|
|
85
|
+
.studio-tile[data-selected='true'] .studio-tile-scrim { opacity: 1; }
|
|
86
|
+
.studio-tile-actions {
|
|
87
|
+
opacity: 0; transform: translateY(-3px);
|
|
88
|
+
transition: opacity .18s ease, transform .18s ease;
|
|
89
|
+
}
|
|
90
|
+
.studio-tile:hover .studio-tile-actions,
|
|
91
|
+
.studio-tile:focus-within .studio-tile-actions { opacity: 1; transform: none; }
|
|
92
|
+
.studio-tile-prompt {
|
|
93
|
+
opacity: 0; transform: translateY(3px);
|
|
94
|
+
transition: opacity .18s ease, transform .18s ease;
|
|
95
|
+
text-shadow: 0 1px 2px rgba(0,0,0,.35);
|
|
96
|
+
}
|
|
97
|
+
.studio-tile:hover .studio-tile-prompt,
|
|
98
|
+
.studio-tile:focus-within .studio-tile-prompt { opacity: 1; transform: none; }
|
|
99
|
+
.studio-tile.studio-has-player .studio-tile-prompt { left: 36px; }
|
|
100
|
+
.studio-ibtn {
|
|
101
|
+
background: rgba(12,13,26,.55); color: #fff;
|
|
102
|
+
-webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
|
|
103
|
+
transition: background .12s ease, color .12s ease;
|
|
104
|
+
}
|
|
105
|
+
.studio-ibtn:hover { background: rgba(12,13,26,.78); }
|
|
106
|
+
.studio-ibtn-danger:hover { color: #ff8095; }
|
|
107
|
+
.studio-ibtn::after {
|
|
108
|
+
content: attr(data-tip); position: absolute; top: calc(100% + 6px); right: 0;
|
|
109
|
+
background: rgba(12,13,26,.92); color: #fff;
|
|
110
|
+
font-size: 11px; line-height: 1; padding: 5px 7px; border-radius: 6px;
|
|
111
|
+
white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity .12s ease;
|
|
112
|
+
}
|
|
113
|
+
.studio-ibtn:hover::after, .studio-ibtn:focus-visible::after { opacity: 1; }
|
|
114
|
+
.studio-chip-dark {
|
|
115
|
+
background: rgba(12,13,26,.55); color: #fff;
|
|
116
|
+
-webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
|
|
117
|
+
}
|
|
118
|
+
.studio-tile-badges > * { transition: opacity .18s ease; }
|
|
119
|
+
.studio-tile:hover .studio-tile-badges > :not(.studio-play-btn),
|
|
120
|
+
.studio-tile:focus-within .studio-tile-badges > :not(.studio-play-btn) { opacity: 0; }
|
|
121
|
+
.studio-tile-sel { display: none; border: 1.5px solid rgba(255,255,255,.9); }
|
|
122
|
+
.studio-tile[data-selectable='true']:hover .studio-tile-sel,
|
|
123
|
+
.studio-tile[data-selectable='true']:focus-within .studio-tile-sel,
|
|
124
|
+
.studio-selectmode .studio-tile-sel { display: grid; }
|
|
125
|
+
.studio-selectmode .studio-tile-actions,
|
|
126
|
+
.studio-selectmode .studio-play-btn { display: none; }
|
|
127
|
+
|
|
128
|
+
/* ── Audio waveform ── two identical bar layers; the played layer revealed
|
|
129
|
+
by clip-path from a --pos custom property written per animation frame.
|
|
130
|
+
No CSS animation, so a pause freezes exactly and reduced-motion has
|
|
131
|
+
nothing to strip. */
|
|
132
|
+
.studio-wave {
|
|
133
|
+
position: absolute; inset: 0; display: flex; align-items: center;
|
|
134
|
+
justify-content: center; gap: 2px; padding: 0 8%;
|
|
135
|
+
}
|
|
136
|
+
.studio-wave i {
|
|
137
|
+
flex: 1 1 0; min-width: 2px; max-width: 8px;
|
|
138
|
+
border-radius: 4px; background: currentColor;
|
|
139
|
+
}
|
|
140
|
+
.studio-wave-layers { position: absolute; inset: 0; }
|
|
141
|
+
.studio-wave-play { clip-path: inset(0 calc((1 - var(--pos, 0)) * 100%) 0 0); }
|
|
142
|
+
.studio-wave-layers[data-active='true'] .studio-wave-base { opacity: .3; }
|
|
143
|
+
|
|
144
|
+
/* ── Skeleton ── sweep while running; reduced motion gets a gentle pulse,
|
|
145
|
+
NOT nothing — the surface must still read as busy. */
|
|
146
|
+
.studio-skeleton {
|
|
147
|
+
position: relative; overflow: hidden; border-radius: 0;
|
|
148
|
+
background: hsl(var(--accent)); aspect-ratio: var(--r, 1.5);
|
|
149
|
+
}
|
|
150
|
+
.studio-skeleton::after {
|
|
151
|
+
content: ''; position: absolute; inset: 0;
|
|
152
|
+
background: linear-gradient(100deg, transparent 18%, hsl(var(--foreground) / .07) 50%, transparent 82%);
|
|
153
|
+
transform: translateX(-100%);
|
|
154
|
+
animation: studio-sweep 1.4s ease-in-out infinite;
|
|
155
|
+
}
|
|
156
|
+
@keyframes studio-sweep { to { transform: translateX(100%); } }
|
|
157
|
+
@keyframes studio-softpulse { 0%, 100% { opacity: .25; } 50% { opacity: .7; } }
|
|
158
|
+
|
|
159
|
+
/* ── Motion ── */
|
|
160
|
+
.studio-rise { animation: studio-rise .34s cubic-bezier(.2,.7,.3,1) both; }
|
|
161
|
+
@keyframes studio-rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
|
|
162
|
+
.studio-toast-in { animation: studio-toast-in .22s cubic-bezier(.2,.7,.3,1) both; }
|
|
163
|
+
.studio-toast-out { animation: studio-toast-out .18s ease both; }
|
|
164
|
+
@keyframes studio-toast-in { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: none; } }
|
|
165
|
+
@keyframes studio-toast-out { to { opacity: 0; transform: translateY(6px); } }
|
|
166
|
+
|
|
167
|
+
/* ── Viewer backdrop / dock ── */
|
|
168
|
+
.studio-backdrop {
|
|
169
|
+
background: rgba(12,13,26,.46);
|
|
170
|
+
-webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
|
|
171
|
+
}
|
|
172
|
+
:root[data-theme='dark'] .studio-backdrop,
|
|
173
|
+
.dark .studio-backdrop { background: rgba(6,6,12,.62); }
|
|
174
|
+
.studio-dock::before {
|
|
175
|
+
content: ''; position: absolute; left: 0; right: 0; bottom: 100%;
|
|
176
|
+
height: 28px; pointer-events: none;
|
|
177
|
+
background: linear-gradient(to top, hsl(var(--background)), transparent);
|
|
178
|
+
}
|
|
179
|
+
/* --pos is written per animation frame by the shared playback state; the
|
|
180
|
+
fill/thumb consume it here so component TSX carries no var() reference
|
|
181
|
+
(the tokens-contract gate treats TSX var() refs as theme tokens). */
|
|
182
|
+
.studio-seek-fill { width: calc(var(--pos, 0) * 100%); }
|
|
183
|
+
.studio-seek-thumb { left: calc(var(--pos, 0) * 100%); }
|
|
184
|
+
.studio-seek:focus-visible {
|
|
185
|
+
outline: 2px solid hsl(var(--primary)); outline-offset: 3px; border-radius: 999px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@media (prefers-reduced-motion: reduce) {
|
|
189
|
+
.studio-rise, .studio-toast-in, .studio-toast-out { animation: none; }
|
|
190
|
+
.studio-skeleton::after {
|
|
191
|
+
transform: none; background: hsl(var(--foreground) / .07);
|
|
192
|
+
animation: studio-softpulse 1.8s ease-in-out infinite;
|
|
193
|
+
}
|
|
124
194
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type Generation } from '../studio/generation';
|
|
2
|
+
export interface UseBatchNavigationOptions {
|
|
3
|
+
/** Rows whose batch keys are treated as already seen. Read once, at mount. */
|
|
4
|
+
seed: readonly Generation[];
|
|
5
|
+
/**
|
|
6
|
+
* A batch key that never navigates, read on every call — the generation
|
|
7
|
+
* screen passes its own batch so rows landing for the batch already on
|
|
8
|
+
* screen stay put while a NEW dock submit navigates.
|
|
9
|
+
*/
|
|
10
|
+
currentBatchKey?: string;
|
|
11
|
+
onOpenGeneration: (batchKey: string, first: Generation) => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Returns a callback the screens wrap around `onGenerated`: the first row of a
|
|
15
|
+
* batch not seen before navigates via `onOpenGeneration`, exactly once per
|
|
16
|
+
* batch — a four-image batch arrives as four rows sharing one batch key and
|
|
17
|
+
* must produce one navigation, and rows merely refreshed from the loader
|
|
18
|
+
* (seeded at mount) must produce none.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useBatchNavigation({ seed, currentBatchKey, onOpenGeneration, }: UseBatchNavigationOptions): (generation: Generation) => void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Generation } from '../studio/generation';
|
|
2
|
+
import type { DeleteGenerations } from '../studio/ports';
|
|
3
|
+
export interface UseDeferredDeleteOptions {
|
|
4
|
+
remove: DeleteGenerations;
|
|
5
|
+
/** Undo window AND toast lifetime. Default 3500. */
|
|
6
|
+
undoWindowMs?: number;
|
|
7
|
+
/** After the server call succeeds for a batch. */
|
|
8
|
+
onCommitted?: (ids: readonly string[]) => void;
|
|
9
|
+
/** After a failed server call restored the rows (screens may refetch). */
|
|
10
|
+
onRestoreFailed?: (ids: readonly string[]) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface DeferredDelete {
|
|
13
|
+
/** Rows the screens must filter OUT of every render. Survives refetches. */
|
|
14
|
+
pendingIds: ReadonlySet<string>;
|
|
15
|
+
request: (generations: readonly Generation[]) => void;
|
|
16
|
+
/** Commit every outstanding batch NOW (unmount/navigation). */
|
|
17
|
+
flush: () => void;
|
|
18
|
+
}
|
|
19
|
+
export declare function useDeferredDelete(options: UseDeferredDeleteOptions): DeferredDelete;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FetchGenerationsPage, GenerationPage, MediaTypeFilter } from '../studio/ports';
|
|
2
|
+
import { type Generation } from '../studio/generation';
|
|
3
|
+
export interface UseGenerationHistoryOptions {
|
|
4
|
+
fetchPage: FetchGenerationsPage;
|
|
5
|
+
/** ALREADY debounced by the caller. */
|
|
6
|
+
q: string;
|
|
7
|
+
type: MediaTypeFilter;
|
|
8
|
+
/** SSR/loader page 1 for the DEFAULT view only (q === '' && type === 'all'). */
|
|
9
|
+
initialPage?: GenerationPage;
|
|
10
|
+
}
|
|
11
|
+
export interface GenerationHistoryState {
|
|
12
|
+
items: Generation[];
|
|
13
|
+
hasMore: boolean;
|
|
14
|
+
isLoadingFirst: boolean;
|
|
15
|
+
isLoadingMore: boolean;
|
|
16
|
+
isError: boolean;
|
|
17
|
+
loadMore: () => void;
|
|
18
|
+
retry: () => void;
|
|
19
|
+
reload: () => void;
|
|
20
|
+
}
|
|
21
|
+
/** Cursor-paged generation history over the product-supplied data port. */
|
|
22
|
+
export declare function useGenerationHistory({ fetchPage, q, type, initialPage, }: UseGenerationHistoryOptions): GenerationHistoryState;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type JSX, type RefObject } from 'react';
|
|
2
|
+
import { type Generation } from '../studio';
|
|
3
|
+
export interface VaultPathPopoverProps {
|
|
4
|
+
open: boolean;
|
|
5
|
+
triggerRef: RefObject<HTMLElement | null>;
|
|
6
|
+
panelRef: RefObject<HTMLDivElement | null>;
|
|
7
|
+
generations: readonly Generation[];
|
|
8
|
+
onSubmit: (path: string) => void | Promise<void>;
|
|
9
|
+
onCancel: () => void;
|
|
10
|
+
pending?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function VaultPathPopover({ open, triggerRef, panelRef, generations, onSubmit, onCancel, pending, }: VaultPathPopoverProps): JSX.Element | null;
|
package/dist/web-react/index.js
CHANGED
|
@@ -36,7 +36,6 @@ import {
|
|
|
36
36
|
RecordGrid,
|
|
37
37
|
RunDrillIn,
|
|
38
38
|
SeatPaywall,
|
|
39
|
-
SessionHistoryPanel,
|
|
40
39
|
Sparkline,
|
|
41
40
|
__resetAttachmentFileCacheForTests,
|
|
42
41
|
activityTone,
|
|
@@ -63,7 +62,6 @@ import {
|
|
|
63
62
|
formatInsightDelta,
|
|
64
63
|
formatModelCost,
|
|
65
64
|
formatRecordGridValue,
|
|
66
|
-
formatSessionTimestamp,
|
|
67
65
|
formatSparklineValue,
|
|
68
66
|
formatTokensPerSecond,
|
|
69
67
|
hasSecretField,
|
|
@@ -121,9 +119,6 @@ import {
|
|
|
121
119
|
useDictation,
|
|
122
120
|
useDurablePlanFlow,
|
|
123
121
|
useFileMentions,
|
|
124
|
-
useInfiniteScroll,
|
|
125
|
-
useSessionActions,
|
|
126
|
-
useSessionHistory,
|
|
127
122
|
useSmoothText,
|
|
128
123
|
useThinkingSeconds,
|
|
129
124
|
validateRecordGridCell,
|
|
@@ -137,7 +132,14 @@ import {
|
|
|
137
132
|
withoutRecordGridCreated,
|
|
138
133
|
withoutRecordGridRemoved,
|
|
139
134
|
withoutRecordGridUpdate
|
|
140
|
-
} from "../chunk-
|
|
135
|
+
} from "../chunk-RZ6MYWMJ.js";
|
|
136
|
+
import {
|
|
137
|
+
SessionHistoryPanel,
|
|
138
|
+
formatSessionTimestamp,
|
|
139
|
+
useInfiniteScroll,
|
|
140
|
+
useSessionActions,
|
|
141
|
+
useSessionHistory
|
|
142
|
+
} from "../chunk-KKBTPZIE.js";
|
|
141
143
|
import "../chunk-FBVLEGEG.js";
|
|
142
144
|
import {
|
|
143
145
|
EvidenceLineageTable,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/agent-app",
|
|
3
|
-
"version": "0.46.
|
|
3
|
+
"version": "0.46.3",
|
|
4
4
|
"packageManager": "pnpm@11.17.0",
|
|
5
5
|
"description": "Build agent applications with typed chat, tools, sandboxes, integrations, billing, and evaluation.",
|
|
6
6
|
"keywords": [
|
|
@@ -502,7 +502,7 @@
|
|
|
502
502
|
"prepare": "pnpm run build",
|
|
503
503
|
"test": "vitest run",
|
|
504
504
|
"test:generated": "node .github/scripts/test-generated-projects.mjs",
|
|
505
|
-
"test:gates": "vitest run src/sandbox/index.test.ts src/profile/index.test.ts tests/chat-stream.test.ts tests/browser-safe-subpaths.test.ts tests/test-quality.test.ts tests/knip-entries-fresh.test.ts tests/web-react/popover-escapes-host.test.tsx tests/web-react/popover-canon-source.test.ts",
|
|
505
|
+
"test:gates": "vitest run src/sandbox/index.test.ts src/profile/index.test.ts tests/chat-stream.test.ts tests/browser-safe-subpaths.test.ts tests/test-quality.test.ts tests/knip-entries-fresh.test.ts tests/web-react/popover-escapes-host.test.tsx tests/web-react/popover-canon-source.test.ts tests/studio/layer-order.test.ts",
|
|
506
506
|
"test:watch": "vitest",
|
|
507
507
|
"typecheck": "tsc --noEmit",
|
|
508
508
|
"docs:gen": "agent-docs",
|
|
@@ -515,18 +515,17 @@
|
|
|
515
515
|
"@cloudflare/workers-types": "5.20260722.1",
|
|
516
516
|
"@firecrawl/pdf-inspector-wasm": "^0.1.3",
|
|
517
517
|
"@hocuspocus/provider": "^4.4.0",
|
|
518
|
-
"@radix-ui/react-dialog": "^1.1.15",
|
|
519
518
|
"@storybook/react": "^10.5.5",
|
|
520
519
|
"@storybook/react-vite": "^10.5.5",
|
|
521
520
|
"@tangle-network/agent-docs": "0.2.1",
|
|
522
|
-
"@tangle-network/agent-eval": "0.
|
|
521
|
+
"@tangle-network/agent-eval": "0.149.0",
|
|
523
522
|
"@tangle-network/agent-integrations": "0.53.54",
|
|
524
|
-
"@tangle-network/agent-interface": "1.0
|
|
525
|
-
"@tangle-network/agent-knowledge": "8.0.
|
|
523
|
+
"@tangle-network/agent-interface": "1.1.0",
|
|
524
|
+
"@tangle-network/agent-knowledge": "8.0.10",
|
|
526
525
|
"@tangle-network/agent-profile-materialize": "0.16.0",
|
|
527
|
-
"@tangle-network/agent-runtime": "0.
|
|
526
|
+
"@tangle-network/agent-runtime": "0.142.0",
|
|
528
527
|
"@tangle-network/brand": "1.5.0",
|
|
529
|
-
"@tangle-network/sandbox": "0.
|
|
528
|
+
"@tangle-network/sandbox": "0.29.0",
|
|
530
529
|
"@tangle-network/sandbox-ui": "0.105.0",
|
|
531
530
|
"@tangle-network/ui": "^11.5.0",
|
|
532
531
|
"@testing-library/dom": "^10.4.1",
|
|
@@ -570,15 +569,14 @@
|
|
|
570
569
|
"peerDependencies": {
|
|
571
570
|
"@firecrawl/pdf-inspector-wasm": ">=0.1.3",
|
|
572
571
|
"@huggingface/transformers": ">=3",
|
|
573
|
-
"@
|
|
574
|
-
"@tangle-network/agent-eval": ">=0.147.0",
|
|
572
|
+
"@tangle-network/agent-eval": ">=0.149.0 <0.150.0",
|
|
575
573
|
"@tangle-network/agent-integrations": ">=0.52.0",
|
|
576
574
|
"@tangle-network/agent-interface": "^1.0.0",
|
|
577
|
-
"@tangle-network/agent-knowledge": "
|
|
575
|
+
"@tangle-network/agent-knowledge": "^8.0.10",
|
|
578
576
|
"@tangle-network/agent-profile-materialize": ">=0.16.0",
|
|
579
|
-
"@tangle-network/agent-runtime": ">=0.
|
|
577
|
+
"@tangle-network/agent-runtime": ">=0.142.0 <0.143.0",
|
|
580
578
|
"@tangle-network/brand": ">=1.5.0",
|
|
581
|
-
"@tangle-network/sandbox": ">=0.
|
|
579
|
+
"@tangle-network/sandbox": ">=0.29.0 <0.30.0",
|
|
582
580
|
"@tangle-network/sandbox-ui": ">=0.105.0",
|
|
583
581
|
"@tangle-network/ui": ">=11.5.0",
|
|
584
582
|
"@xyflow/react": ">=12.0.0",
|
|
@@ -645,9 +643,6 @@
|
|
|
645
643
|
"lucide-react": {
|
|
646
644
|
"optional": true
|
|
647
645
|
},
|
|
648
|
-
"@radix-ui/react-dialog": {
|
|
649
|
-
"optional": true
|
|
650
|
-
},
|
|
651
646
|
"resend": {
|
|
652
647
|
"optional": true
|
|
653
648
|
},
|