@agent-native/core 0.131.3 → 0.131.5
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/docs/content/docs-components.mdx +491 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AgentPanel.tsx +58 -34
- package/corpus/core/src/localization/default-messages.ts +6 -0
- package/corpus/templates/content/.agents/skills/content-product-development/SKILL.md +47 -0
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +3 -1
- package/corpus/templates/design/actions/get-design.ts +11 -3
- package/corpus/templates/design/actions/present-design-variants.ts +62 -5
- package/corpus/templates/design/app/components/design/BreakpointBar.tsx +64 -1
- package/corpus/templates/design/app/components/design/EditPanel.tsx +199 -25
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +160 -46
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +7 -1
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +59 -12
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +1 -1
- package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +34 -29
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +2 -2
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +28 -10
- package/corpus/templates/design/app/components/design/inspector/design-icons.tsx +15 -28
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +4 -3
- package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +12 -8
- package/corpus/templates/design/app/components/design/multi-screen/culling.ts +34 -9
- package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +13 -27
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +8 -1
- package/corpus/templates/design/app/components/design/multi-screen/zoom-gesture.ts +95 -0
- package/corpus/templates/design/app/i18n-data.ts +121 -10
- package/corpus/templates/design/app/pages/design-editor/data-operations.ts +45 -0
- package/corpus/templates/design/app/pages/design-editor/layout-operations.ts +59 -0
- package/corpus/templates/design/app/pages/design-editor/text-edit-utils.ts +115 -40
- package/corpus/templates/design/changelog/2026-07-28-generated-design-variants-no-longer-overlap-each-other-on-th.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-auto-layout-direction-icons-show-an-arrow.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-breakpoints-no-longer-overlap-neighbouring-screens.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-canvas-is-more-responsive-when-selecting-and-adding-frames.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-canvas-zoom-is-smoother-and-labels-hold-their-size.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-double-click-a-frame-now-drills-into-its-layers.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-export-preview-shows-the-real-render.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-figma-parity-shortcuts.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-more-breakpoint-width-presets.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-resize-a-selected-frame-to-a-device-preset.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-text-tool-reaches-the-right-surface.md +6 -0
- package/corpus/templates/design/shared/canvas-frames.ts +23 -0
- package/corpus/templates/design/shared/code-layer.ts +54 -2
- package/corpus/templates/design/shared/responsive-frame-layout.ts +55 -0
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +16 -5
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/localization/default-messages.d.ts +6 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +6 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/docs/content/docs-components.mdx +491 -0
- package/package.json +1 -1
- package/src/client/AgentPanel.tsx +58 -34
- package/src/localization/default-messages.ts +6 -0
|
@@ -1,7 +1,38 @@
|
|
|
1
1
|
import { POINTER_TEXT_EDIT_ACTIVATION_DELAY_MS } from "@/components/design/design-canvas/pending-text-edit";
|
|
2
|
+
import { findCanvasIframeForScreen } from "@/components/design/multi-screen/iframe-targeting";
|
|
2
3
|
|
|
3
4
|
import { queryUniqueSelector } from "./dom-utils";
|
|
4
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Why one begin-text-edit attempt did not end up in an editing session. These
|
|
8
|
+
* stay distinct on purpose: a screen whose iframe is not mounted yet
|
|
9
|
+
* ("no-iframe") is a retry-worthy race, while an iframe that answered and does
|
|
10
|
+
* not have the node ("node-missing") or has it un-focused ("not-editing") is a
|
|
11
|
+
* different situation entirely. Collapsing them all to `false` is what made the
|
|
12
|
+
* board-space text bug invisible — every probe failed identically, so nothing
|
|
13
|
+
* distinguished "asked the wrong window" from "user has not typed yet".
|
|
14
|
+
*/
|
|
15
|
+
export type BeginTextEditOutcome =
|
|
16
|
+
| "active"
|
|
17
|
+
| "done"
|
|
18
|
+
| "node-missing"
|
|
19
|
+
| "not-editing"
|
|
20
|
+
| "no-iframe"
|
|
21
|
+
| "no-reply"
|
|
22
|
+
/** begin-text-edit was posted; the iframe has not been re-probed yet. Never
|
|
23
|
+
* settle on this — it is not evidence the node was abandoned, and the
|
|
24
|
+
* caller's exhaustion path deletes untouched nodes. */
|
|
25
|
+
| "activation-requested";
|
|
26
|
+
|
|
27
|
+
export function isTextEditSessionOutcome(
|
|
28
|
+
outcome: BeginTextEditOutcome,
|
|
29
|
+
): boolean {
|
|
30
|
+
return outcome === "active" || outcome === "done";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Grace period before re-probing a just-requested activation. */
|
|
34
|
+
const ACTIVATION_CONFIRM_DELAY_MS = 300;
|
|
35
|
+
|
|
5
36
|
/**
|
|
6
37
|
* Ask a single iframe's editor-chrome bridge whether a text-edit session for
|
|
7
38
|
* `nodeId` is "active" (focused), "done" (non-empty committed text), or
|
|
@@ -13,16 +44,16 @@ import { queryUniqueSelector } from "./dom-utils";
|
|
|
13
44
|
function queryTextEditStatus(
|
|
14
45
|
iframe: HTMLIFrameElement,
|
|
15
46
|
nodeId: string,
|
|
16
|
-
): Promise<"active" | "done" |
|
|
47
|
+
): Promise<"active" | "done" | "node-missing" | "not-editing" | "no-reply"> {
|
|
17
48
|
const win = iframe.contentWindow;
|
|
18
|
-
if (!win) return Promise.resolve(
|
|
49
|
+
if (!win) return Promise.resolve("no-reply");
|
|
19
50
|
const correlationId = `text-edit-status-${Date.now()}-${Math.random()
|
|
20
51
|
.toString(36)
|
|
21
52
|
.slice(2, 6)}`;
|
|
22
53
|
return new Promise((resolve) => {
|
|
23
54
|
const timer = window.setTimeout(() => {
|
|
24
55
|
window.removeEventListener("message", listener);
|
|
25
|
-
resolve(
|
|
56
|
+
resolve("no-reply");
|
|
26
57
|
}, 250);
|
|
27
58
|
const listener = (event: MessageEvent) => {
|
|
28
59
|
if (
|
|
@@ -38,7 +69,13 @@ function queryTextEditStatus(
|
|
|
38
69
|
window.clearTimeout(timer);
|
|
39
70
|
window.removeEventListener("message", listener);
|
|
40
71
|
const status = event.data.status;
|
|
41
|
-
|
|
72
|
+
if (status === "active" || status === "done") {
|
|
73
|
+
resolve(status);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Older bridges answer a bare `false` for both cases; treat that as
|
|
77
|
+
// "present but not editing" so the retry ladder behaves as before.
|
|
78
|
+
resolve(status === "missing" ? "node-missing" : "not-editing");
|
|
42
79
|
};
|
|
43
80
|
window.addEventListener("message", listener);
|
|
44
81
|
win.postMessage(
|
|
@@ -48,31 +85,50 @@ function queryTextEditStatus(
|
|
|
48
85
|
});
|
|
49
86
|
}
|
|
50
87
|
|
|
51
|
-
async function
|
|
88
|
+
async function probeTextEdit(
|
|
89
|
+
screenId: string | null,
|
|
90
|
+
nodeId: string,
|
|
91
|
+
boardFileId: string | null,
|
|
92
|
+
): Promise<BeginTextEditOutcome> {
|
|
93
|
+
if (typeof document === "undefined" || !nodeId || !screenId) {
|
|
94
|
+
return "no-iframe";
|
|
95
|
+
}
|
|
96
|
+
// The board surface's live iframe carries no `data-screen-iframe-id`, so the
|
|
97
|
+
// old `dataset.screenIframeId === screenId` filter never matched it and every
|
|
98
|
+
// attempt fell through to broadcasting at all the *screen* iframes — none of
|
|
99
|
+
// which own a board node. findCanvasIframeForScreen is the one resolver that
|
|
100
|
+
// already knows the board's `[data-board-surface-layer]` shape.
|
|
101
|
+
const iframe = findCanvasIframeForScreen(
|
|
102
|
+
document.body,
|
|
103
|
+
screenId,
|
|
104
|
+
boardFileId ?? undefined,
|
|
105
|
+
);
|
|
106
|
+
if (!iframe?.contentWindow) return "no-iframe";
|
|
107
|
+
return queryTextEditStatus(iframe, nodeId);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Probes, and asks the iframe to enter edit mode when it is not already
|
|
111
|
+
* editing. Reports `activation-requested` rather than the status observed
|
|
112
|
+
* *before* the request: that pre-activation status is not an answer about the
|
|
113
|
+
* session it just asked for. */
|
|
114
|
+
async function requestTextEdit(
|
|
52
115
|
screenId: string | null,
|
|
53
116
|
nodeId: string,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
117
|
+
boardFileId: string | null,
|
|
118
|
+
): Promise<BeginTextEditOutcome> {
|
|
119
|
+
const status = await probeTextEdit(screenId, nodeId, boardFileId);
|
|
120
|
+
if (isTextEditSessionOutcome(status) || status === "no-iframe") return status;
|
|
121
|
+
const iframe = findCanvasIframeForScreen(
|
|
122
|
+
document.body,
|
|
123
|
+
screenId ?? "",
|
|
124
|
+
boardFileId ?? undefined,
|
|
60
125
|
);
|
|
61
|
-
|
|
62
|
-
|
|
126
|
+
if (!iframe?.contentWindow) return "no-iframe";
|
|
127
|
+
iframe.contentWindow.postMessage(
|
|
128
|
+
{ type: "begin-text-edit", nodeId, force: true },
|
|
129
|
+
"*",
|
|
63
130
|
);
|
|
64
|
-
|
|
65
|
-
for (const iframe of orderedIframes) {
|
|
66
|
-
const status = await queryTextEditStatus(iframe, nodeId);
|
|
67
|
-
if (status === "active" || status === "done") return status;
|
|
68
|
-
}
|
|
69
|
-
orderedIframes.forEach((iframe) => {
|
|
70
|
-
iframe.contentWindow?.postMessage(
|
|
71
|
-
{ type: "begin-text-edit", nodeId, force: true },
|
|
72
|
-
"*",
|
|
73
|
-
);
|
|
74
|
-
});
|
|
75
|
-
return false;
|
|
131
|
+
return "activation-requested";
|
|
76
132
|
}
|
|
77
133
|
|
|
78
134
|
/**
|
|
@@ -93,13 +149,20 @@ async function postBeginTextEditToPreviewIframes(
|
|
|
93
149
|
export function scheduleBeginTextEditForScreen(
|
|
94
150
|
screenId: string | null,
|
|
95
151
|
nodeId: string,
|
|
96
|
-
|
|
152
|
+
options?: {
|
|
153
|
+
/** Board file id, so a board-space text node resolves the board surface's
|
|
154
|
+
* iframe instead of looking for a `data-screen-iframe-id` it never has. */
|
|
155
|
+
boardFileId?: string | null;
|
|
156
|
+
onExhausted?: (finalStatus: BeginTextEditOutcome) => void;
|
|
157
|
+
},
|
|
97
158
|
): () => void {
|
|
98
159
|
if (typeof window === "undefined") return () => {};
|
|
160
|
+
const onExhausted = options?.onExhausted;
|
|
161
|
+
const boardFileId = options?.boardFileId ?? null;
|
|
99
162
|
let finished = false;
|
|
100
|
-
let lastStatus:
|
|
163
|
+
let lastStatus: BeginTextEditOutcome = "no-iframe";
|
|
101
164
|
const timers: number[] = [];
|
|
102
|
-
const settle = (status:
|
|
165
|
+
const settle = (status: BeginTextEditOutcome) => {
|
|
103
166
|
if (finished) return;
|
|
104
167
|
finished = true;
|
|
105
168
|
lastStatus = status;
|
|
@@ -119,19 +182,31 @@ export function scheduleBeginTextEditForScreen(
|
|
|
119
182
|
delays.forEach((delay, index) => {
|
|
120
183
|
const timer = window.setTimeout(() => {
|
|
121
184
|
if (finished) return;
|
|
122
|
-
void
|
|
123
|
-
(
|
|
185
|
+
void requestTextEdit(screenId, nodeId, boardFileId).then((status) => {
|
|
186
|
+
if (finished) return;
|
|
187
|
+
lastStatus = status;
|
|
188
|
+
if (isTextEditSessionOutcome(status)) {
|
|
189
|
+
settle(status);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (index !== delays.length - 1) return;
|
|
193
|
+
if (status !== "activation-requested") {
|
|
194
|
+
settle(status);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
// Activation is still in flight, and the caller deletes untouched nodes
|
|
198
|
+
// on exhaustion. Settle on what the iframe reports, never on the request.
|
|
199
|
+
const confirmTimer = window.setTimeout(() => {
|
|
124
200
|
if (finished) return;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
);
|
|
201
|
+
void probeTextEdit(screenId, nodeId, boardFileId).then(
|
|
202
|
+
(confirmed) => {
|
|
203
|
+
if (finished) return;
|
|
204
|
+
settle(confirmed);
|
|
205
|
+
},
|
|
206
|
+
);
|
|
207
|
+
}, ACTIVATION_CONFIRM_DELAY_MS);
|
|
208
|
+
timers.push(confirmTimer);
|
|
209
|
+
});
|
|
135
210
|
}, delay);
|
|
136
211
|
timers.push(timer);
|
|
137
212
|
});
|
|
@@ -58,6 +58,29 @@ export function parseCanvasFrameGeometryById(
|
|
|
58
58
|
);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/** Y a new group must start at to clear existing frames, or 0 when the board is
|
|
62
|
+
* empty. Placing at y=0 unconditionally stacks each new group on the last. */
|
|
63
|
+
export function nextFreeCanvasRowY(
|
|
64
|
+
existing: unknown,
|
|
65
|
+
gap: number,
|
|
66
|
+
options: { ignoreFileIds?: readonly string[] } = {},
|
|
67
|
+
): number {
|
|
68
|
+
const ignored = new Set(options.ignoreFileIds ?? []);
|
|
69
|
+
const frames = Object.entries(parseCanvasFrameGeometryById(existing)).filter(
|
|
70
|
+
([id]) => !ignored.has(id),
|
|
71
|
+
);
|
|
72
|
+
let bottom = 0;
|
|
73
|
+
let sawFrame = false;
|
|
74
|
+
for (const [, frame] of frames) {
|
|
75
|
+
const y = frame.y ?? 0;
|
|
76
|
+
const height = frame.height ?? 0;
|
|
77
|
+
if (!Number.isFinite(y) || !Number.isFinite(height)) continue;
|
|
78
|
+
sawFrame = true;
|
|
79
|
+
bottom = Math.max(bottom, y + height);
|
|
80
|
+
}
|
|
81
|
+
return sawFrame ? bottom + gap : 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
61
84
|
export function mergeCanvasFramePlacements({
|
|
62
85
|
existing,
|
|
63
86
|
placements,
|
|
@@ -2230,6 +2230,38 @@ function buildProjection(
|
|
|
2230
2230
|
};
|
|
2231
2231
|
}
|
|
2232
2232
|
|
|
2233
|
+
/**
|
|
2234
|
+
* Most-recently-used projections, keyed by the exact document they came from.
|
|
2235
|
+
*
|
|
2236
|
+
* Projecting means a full HTML parse plus a tree walk, and the editor calls
|
|
2237
|
+
* this on nearly every selection and content change from dozens of call sites —
|
|
2238
|
+
* repeatedly, synchronously, on the main thread, for documents that have not
|
|
2239
|
+
* changed. That is the canvas "freezing while just clicking" and the sluggish
|
|
2240
|
+
* cursor.
|
|
2241
|
+
*
|
|
2242
|
+
* Keyed on the whole HTML string rather than a hash on purpose: a 32-bit hash
|
|
2243
|
+
* collision would hand a caller a confidently wrong layer tree, and every
|
|
2244
|
+
* id-keyed edit downstream would target the wrong node. The string is already
|
|
2245
|
+
* retained by the file/query cache, so the key costs a reference, not a copy.
|
|
2246
|
+
*
|
|
2247
|
+
* Callers must treat the result as read-only — it is shared now. Every consumer
|
|
2248
|
+
* only reads (`find`/`filter`/`map`); `applyCodeLayer*`-style writers build new
|
|
2249
|
+
* HTML and re-project rather than editing a projection in place.
|
|
2250
|
+
*/
|
|
2251
|
+
const PROJECTION_CACHE_MAX = 24;
|
|
2252
|
+
const projectionCache = new Map<string, CodeLayerProjection>();
|
|
2253
|
+
|
|
2254
|
+
/** Every own field of the source, sorted, so adding a field to
|
|
2255
|
+
* CodeLayerSource later cannot silently start returning a projection whose
|
|
2256
|
+
* `.source` came from a different call. */
|
|
2257
|
+
function projectionSourceKey(source: CodeLayerSource): string {
|
|
2258
|
+
const record = source as unknown as Record<string, unknown>;
|
|
2259
|
+
return Object.keys(source)
|
|
2260
|
+
.sort()
|
|
2261
|
+
.map((field) => `${field}=${record[field] ?? ""}`)
|
|
2262
|
+
.join("\u0000");
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2233
2265
|
export function buildCodeLayerProjection(
|
|
2234
2266
|
html: string,
|
|
2235
2267
|
options: { source?: CodeLayerSource } = {},
|
|
@@ -2238,8 +2270,28 @@ export function buildCodeLayerProjection(
|
|
|
2238
2270
|
// (e.g. `activeContent` is briefly undefined on first render). Projecting a
|
|
2239
2271
|
// non-string must yield an empty projection, never crash the editor.
|
|
2240
2272
|
const safeHtml = typeof html === "string" ? html : "";
|
|
2241
|
-
|
|
2242
|
-
|
|
2273
|
+
const source = options.source ?? { kind: "inline-html" };
|
|
2274
|
+
const key = `${projectionSourceKey(source)}\u0000${safeHtml}`;
|
|
2275
|
+
const cached = projectionCache.get(key);
|
|
2276
|
+
if (cached) {
|
|
2277
|
+
// Re-insert so the Map's insertion order stays least-recently-used first.
|
|
2278
|
+
projectionCache.delete(key);
|
|
2279
|
+
projectionCache.set(key, cached);
|
|
2280
|
+
return cached;
|
|
2281
|
+
}
|
|
2282
|
+
const projection = buildProjection(safeHtml, source).projection;
|
|
2283
|
+
projectionCache.set(key, projection);
|
|
2284
|
+
if (projectionCache.size > PROJECTION_CACHE_MAX) {
|
|
2285
|
+
const oldest = projectionCache.keys().next();
|
|
2286
|
+
if (!oldest.done) projectionCache.delete(oldest.value);
|
|
2287
|
+
}
|
|
2288
|
+
return projection;
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
/** Drops every cached projection. Exists for tests that assert projection
|
|
2292
|
+
* identity/eviction; production has no reason to call it. */
|
|
2293
|
+
export function clearCodeLayerProjectionCache(): void {
|
|
2294
|
+
projectionCache.clear();
|
|
2243
2295
|
}
|
|
2244
2296
|
|
|
2245
2297
|
export function ensureCodeLayerNodeIdsInHtml(
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Geometry of one screen's responsive frame group: the primary frame plus the
|
|
3
|
+
* breakpoint previews painted to its right.
|
|
4
|
+
*
|
|
5
|
+
* This lives in `shared/` because both sides of the contract need it. Actions
|
|
6
|
+
* that *place* screens on the overview board (present-design-variants) must
|
|
7
|
+
* reserve the same width the canvas will actually *paint*, or the next screen
|
|
8
|
+
* lands underneath the previous screen's breakpoint row. Keeping the width in
|
|
9
|
+
* one place is what makes writer and renderer agree.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Gap between the primary frame and each breakpoint preview beside it. */
|
|
13
|
+
export const BREAKPOINT_FRAME_GAP = 24;
|
|
14
|
+
|
|
15
|
+
/** Drops any breakpoint whose width equals the primary frame's own width — a
|
|
16
|
+
* redundant duplicate of the base — also cleaning up designs authored before
|
|
17
|
+
* the default set excluded the primary width. */
|
|
18
|
+
export function visibleBreakpointWidths(
|
|
19
|
+
breakpointWidths: readonly number[] | undefined,
|
|
20
|
+
primaryWidthPx: number | undefined,
|
|
21
|
+
): number[] {
|
|
22
|
+
const deduped = Array.from(
|
|
23
|
+
new Set(
|
|
24
|
+
(breakpointWidths ?? []).filter(
|
|
25
|
+
(width) => Number.isFinite(width) && width > 0,
|
|
26
|
+
),
|
|
27
|
+
),
|
|
28
|
+
);
|
|
29
|
+
if (primaryWidthPx === undefined || !Number.isFinite(primaryWidthPx)) {
|
|
30
|
+
return deduped;
|
|
31
|
+
}
|
|
32
|
+
return deduped.filter((width) => Math.abs(width - primaryWidthPx) > 1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Total painted width of a screen's frame group: the primary box plus every
|
|
37
|
+
* breakpoint preview beside it, each drawn at the primary's own uniform
|
|
38
|
+
* `scale`. `visibleWidths` must already be filtered through
|
|
39
|
+
* `visibleBreakpointWidths` — callers differ in which width they dedupe
|
|
40
|
+
* against, but the arithmetic must not.
|
|
41
|
+
*/
|
|
42
|
+
export function getResponsiveGroupWidth({
|
|
43
|
+
primaryWidth,
|
|
44
|
+
scale,
|
|
45
|
+
visibleWidths,
|
|
46
|
+
}: {
|
|
47
|
+
primaryWidth: number;
|
|
48
|
+
scale: number;
|
|
49
|
+
visibleWidths: readonly number[];
|
|
50
|
+
}): number {
|
|
51
|
+
return visibleWidths.reduce(
|
|
52
|
+
(total, width) => total + BREAKPOINT_FRAME_GAP + width * scale,
|
|
53
|
+
Math.max(1, primaryWidth),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentPanel.d.ts","sourceRoot":"","sources":["../../src/client/AgentPanel.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"AgentPanel.d.ts","sourceRoot":"","sources":["../../src/client/AgentPanel.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1245,14 +1245,25 @@ class AgentPanelErrorBoundary extends React.Component {
|
|
|
1245
1245
|
return this.props.children;
|
|
1246
1246
|
if (assistantUiRecoverableRenderErrorKind(this.state.error) &&
|
|
1247
1247
|
this.state.staleIndexRecoveryCount < 2) {
|
|
1248
|
-
return
|
|
1248
|
+
return _jsx(AgentPanelReloadingNotice, {});
|
|
1249
1249
|
}
|
|
1250
|
-
return (
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1250
|
+
return (_jsx(AgentPanelErrorFallback, { details: this.state.error.message, onReset: () => {
|
|
1251
|
+
this.setState({ error: null, staleIndexRecoveryCount: 0 });
|
|
1252
|
+
this.props.onReset();
|
|
1253
|
+
} }));
|
|
1254
1254
|
}
|
|
1255
1255
|
}
|
|
1256
|
+
// The boundary must stay a class (componentDidCatch), but its copy still has to
|
|
1257
|
+
// come from the catalog like every other string in this file — so the fallback
|
|
1258
|
+
// UI lives in function components that can call useT.
|
|
1259
|
+
function AgentPanelReloadingNotice() {
|
|
1260
|
+
const t = useT();
|
|
1261
|
+
return (_jsx("div", { className: "flex h-full items-center justify-center p-6 text-center text-xs text-muted-foreground", children: t("agentPanel.uiError.reloading") }));
|
|
1262
|
+
}
|
|
1263
|
+
function AgentPanelErrorFallback({ details, onReset, }) {
|
|
1264
|
+
const t = useT();
|
|
1265
|
+
return (_jsxs("div", { className: "flex h-full flex-col items-center justify-center gap-3 p-6 text-center", children: [_jsxs("div", { className: "max-w-[260px] space-y-1", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: t("agentPanel.uiError.title") }), _jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: t("agentPanel.uiError.description") })] }), _jsx("button", { type: "button", className: "rounded-md border border-border bg-background px-3 py-1.5 text-xs font-medium text-foreground hover:bg-accent", onClick: onReset, children: t("agentPanel.uiError.reset") }), _jsx(ErrorReportActions, { appName: "Agent panel", title: t("agentPanel.uiError.title"), details: details, issueTitle: "Agent panel UI error", className: "max-w-[260px]", feedbackClassName: "h-7", githubClassName: "h-7" })] }));
|
|
1266
|
+
}
|
|
1256
1267
|
export function AgentPanel(props) {
|
|
1257
1268
|
const [resetKey, setResetKey] = useState(0);
|
|
1258
1269
|
const resetPanel = useCallback(() => {
|