@agimon-ai/po-author 0.0.0-alpha.2 → 0.0.0-alpha.21
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/LICENSE +149 -21
- package/README.md +5 -3
- package/dist/apiContracts.cjs +3 -3
- package/dist/apiContracts.cjs.map +1 -1
- package/dist/apiContracts.mjs +3 -3
- package/dist/apiContracts.mjs.map +1 -1
- package/dist/{authorFacade-BYvlm8Ff.cjs → authorFacade-BkIMF1lR.cjs} +2 -2
- package/dist/authorFacade-BkIMF1lR.cjs.map +1 -0
- package/dist/{authorFacade--Lc599Ft.mjs → authorFacade-CpLG3duk.mjs} +3 -3
- package/dist/authorFacade-CpLG3duk.mjs.map +1 -0
- package/dist/{authorFacade-89Eb2-sW.d.mts → authorFacade-DCTTx7EE.d.mts} +3 -3
- package/dist/{authorFacade-89Eb2-sW.d.mts.map → authorFacade-DCTTx7EE.d.mts.map} +1 -1
- package/dist/authorFacade.cjs +1 -1
- package/dist/authorFacade.d.mts +1 -1
- package/dist/authorFacade.mjs +1 -1
- package/dist/extensions/server.cjs +104 -40
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts +2 -2
- package/dist/extensions/server.d.mts.map +1 -1
- package/dist/extensions/server.mjs +101 -37
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/extensions/web.mjs +797 -385
- package/dist/extensions/web.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +6 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/{structuredDocuments-B914GLxx.mjs → structuredDocuments-BAvFI8VH.mjs} +9 -7
- package/dist/structuredDocuments-BAvFI8VH.mjs.map +1 -0
- package/dist/{structuredDocuments-CNac1JmJ.cjs → structuredDocuments-OYXnnCrZ.cjs} +18 -10
- package/dist/structuredDocuments-OYXnnCrZ.cjs.map +1 -0
- package/package.json +18 -17
- package/src/prompts/po-use-author/SKILL.md +1 -1
- package/dist/authorFacade--Lc599Ft.mjs.map +0 -1
- package/dist/authorFacade-BYvlm8Ff.cjs.map +0 -1
- package/dist/structuredDocuments-B914GLxx.mjs.map +0 -1
- package/dist/structuredDocuments-CNac1JmJ.cjs.map +0 -1
package/dist/extensions/web.mjs
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { apiResponse, createApiClient, defineApiRoutes, defineChannelFile, defineDockFace, defineFileLinks, defineFill, defineGlobalStore, defineLeaderBinding, defineMinorModeFile, defineSessionStore, defineSlotFile, defineToolRenderer, defineWebLifecycle, defineWebPlugin, sessionApiAddress, toolResultTextLines } from "@agimon-ai/po-core/web";
|
|
2
2
|
import { Store } from "@tanstack/store";
|
|
3
3
|
import { useStore } from "@tanstack/react-store";
|
|
4
|
-
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
-
import { Button, CodeEditor, Markdown, MediaPreview, MessageItem, MessageItemBody, MessageItemHeader, MessageItemStatus, MessageLines, collapseLines, toolTone } from "@agimon-ai/po-web-components";
|
|
6
|
+
import { Button, CodeEditor, Dot, FileIcon, Markdown, MediaPreview, MessageItem, MessageItemBody, MessageItemHeader, MessageItemStatus, MessageLines, SparklesIcon, StatusBadge, TrashIcon, collapseLines, toolTone } from "@agimon-ai/po-web-components";
|
|
7
7
|
import { sealedTransport } from "@agimon-ai/po-web-security/browser";
|
|
8
8
|
//#region src/types/webAuthor.ts
|
|
9
9
|
const authorChannelType = "author_webmcp";
|
|
10
10
|
//#endregion
|
|
11
|
+
//#region src/types/author.ts
|
|
12
|
+
const AUTHOR_MODE_ID = "author";
|
|
13
|
+
//#endregion
|
|
11
14
|
//#region src/extensions/workspaces/sessions/(frontend)/_lib/authorRuntime.ts
|
|
12
15
|
const AUTHOR_RUNTIME_BINDING_IDS = {
|
|
13
16
|
text: "author.viewport.text",
|
|
@@ -157,7 +160,9 @@ const documentProfiles = (profiles) => profiles.map((profile) => ({
|
|
|
157
160
|
var AuthorBrowserBridge = class {
|
|
158
161
|
#host;
|
|
159
162
|
#canvases = /* @__PURE__ */ new Map();
|
|
163
|
+
#parked = /* @__PURE__ */ new Map();
|
|
160
164
|
#opening = /* @__PURE__ */ new Map();
|
|
165
|
+
#modeWatches = /* @__PURE__ */ new Map();
|
|
161
166
|
#generation = 0;
|
|
162
167
|
#disposed = false;
|
|
163
168
|
#releaseConnected;
|
|
@@ -173,27 +178,61 @@ var AuthorBrowserBridge = class {
|
|
|
173
178
|
}
|
|
174
179
|
async open(sessionId, alias, profiles) {
|
|
175
180
|
if (this.#disposed) throw new Error("Author browser bridge is disposed");
|
|
181
|
+
this.#watch(sessionId);
|
|
176
182
|
const key = canvasKey(sessionId, alias);
|
|
177
183
|
if (this.#canvases.has(key)) return;
|
|
184
|
+
const parkedCanvas = this.#parked.get(key);
|
|
185
|
+
if (parkedCanvas !== void 0) {
|
|
186
|
+
if (this.#agentEnabled(sessionId)) await this.#openLive(parkedCanvas);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
const pending = this.#opening.get(key);
|
|
190
|
+
if (pending !== void 0) return await pending.promise;
|
|
191
|
+
const parked = {
|
|
192
|
+
sessionId,
|
|
193
|
+
alias,
|
|
194
|
+
profiles: documentProfiles(profiles)
|
|
195
|
+
};
|
|
196
|
+
if (!this.#agentEnabled(sessionId)) {
|
|
197
|
+
this.#parked.set(key, parked);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
await this.#openLive(parked);
|
|
201
|
+
}
|
|
202
|
+
async #openLive(parked) {
|
|
203
|
+
const { sessionId, alias, profiles } = parked;
|
|
204
|
+
const key = canvasKey(sessionId, alias);
|
|
205
|
+
if (this.#disposed || this.#canvases.has(key)) return;
|
|
178
206
|
const pending = this.#opening.get(key);
|
|
179
207
|
if (pending !== void 0) return await pending.promise;
|
|
208
|
+
if (!this.#agentEnabled(sessionId)) {
|
|
209
|
+
this.#parked.set(key, parked);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
this.#parked.delete(key);
|
|
180
213
|
const runtime = new AuthorRuntime(this.#host);
|
|
181
214
|
const generation = ++this.#generation;
|
|
182
|
-
const base = documentProfiles(profiles);
|
|
183
215
|
let opening;
|
|
184
216
|
opening = (async () => {
|
|
185
|
-
|
|
186
|
-
|
|
217
|
+
let release;
|
|
218
|
+
try {
|
|
219
|
+
release = await runtime.replaceProfiles(profiles);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
if (this.#disposed || !this.#agentEnabled(sessionId) || this.#opening.get(key)?.promise !== opening) return;
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
if (this.#disposed || !this.#agentEnabled(sessionId) || this.#canvases.has(key) || this.#opening.get(key)?.promise !== opening) {
|
|
187
225
|
release();
|
|
188
226
|
runtime.dispose();
|
|
227
|
+
if (!this.#disposed && !this.#agentEnabled(sessionId)) this.#parked.set(key, parked);
|
|
189
228
|
return;
|
|
190
229
|
}
|
|
191
230
|
const canvas = {
|
|
192
231
|
sessionId,
|
|
193
232
|
alias,
|
|
194
233
|
generation,
|
|
195
|
-
profiles
|
|
196
|
-
documentProfiles:
|
|
234
|
+
profiles,
|
|
235
|
+
documentProfiles: profiles,
|
|
197
236
|
runtime,
|
|
198
237
|
release,
|
|
199
238
|
pending: /* @__PURE__ */ new Map(),
|
|
@@ -206,7 +245,8 @@ var AuthorBrowserBridge = class {
|
|
|
206
245
|
})();
|
|
207
246
|
this.#opening.set(key, {
|
|
208
247
|
promise: opening,
|
|
209
|
-
runtime
|
|
248
|
+
runtime,
|
|
249
|
+
parked
|
|
210
250
|
});
|
|
211
251
|
try {
|
|
212
252
|
await opening;
|
|
@@ -216,6 +256,7 @@ var AuthorBrowserBridge = class {
|
|
|
216
256
|
}
|
|
217
257
|
async focus(sessionId, alias, profiles) {
|
|
218
258
|
await this.open(sessionId, alias, profiles);
|
|
259
|
+
if (!this.#agentEnabled(sessionId)) return () => void 0;
|
|
219
260
|
const canvas = this.#canvases.get(canvasKey(sessionId, alias));
|
|
220
261
|
if (canvas === void 0) return () => void 0;
|
|
221
262
|
canvas.visible = true;
|
|
@@ -229,21 +270,28 @@ var AuthorBrowserBridge = class {
|
|
|
229
270
|
};
|
|
230
271
|
}
|
|
231
272
|
drop(sessionId, alias) {
|
|
232
|
-
for (const [key, entry] of this.#opening)
|
|
273
|
+
for (const [key, entry] of this.#opening) {
|
|
274
|
+
if (entry.parked.sessionId !== sessionId || alias !== void 0 && entry.parked.alias !== alias) continue;
|
|
233
275
|
this.#opening.delete(key);
|
|
234
276
|
entry.runtime.dispose();
|
|
235
277
|
}
|
|
278
|
+
for (const [key, parked] of this.#parked) {
|
|
279
|
+
if (parked.sessionId !== sessionId || alias !== void 0 && parked.alias !== alias) continue;
|
|
280
|
+
this.#parked.delete(key);
|
|
281
|
+
}
|
|
236
282
|
for (const canvas of this.#canvases.values()) {
|
|
237
283
|
if (canvas.sessionId !== sessionId || alias !== void 0 && canvas.alias !== alias) continue;
|
|
238
|
-
this.#
|
|
239
|
-
this.#release(canvas);
|
|
240
|
-
canvas.runtime.dispose();
|
|
284
|
+
this.#dropLive(canvas);
|
|
241
285
|
}
|
|
242
286
|
}
|
|
243
287
|
apply(sessionId, message) {
|
|
244
|
-
if (message.alias === void 0) return;
|
|
288
|
+
if (!this.#agentEnabled(sessionId) || message.alias === void 0) return;
|
|
245
289
|
const canvas = this.#canvases.get(canvasKey(sessionId, message.alias));
|
|
246
|
-
if (canvas === void 0
|
|
290
|
+
if (canvas === void 0) return;
|
|
291
|
+
if (message.kind === "rejected") {
|
|
292
|
+
console.warn(`Author canvas '${canvas.alias}' was rejected: ${message.reason}`);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
247
295
|
if (message.generation !== canvas.generation || message.ownerToken !== canvas.ownerToken && canvas.ownerToken !== void 0) return;
|
|
248
296
|
if (message.kind === "accepted") {
|
|
249
297
|
canvas.ownerToken = message.ownerToken;
|
|
@@ -310,17 +358,54 @@ var AuthorBrowserBridge = class {
|
|
|
310
358
|
if (this.#disposed) return;
|
|
311
359
|
this.#disposed = true;
|
|
312
360
|
this.#releaseConnected();
|
|
313
|
-
for (const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
this.#canvases.clear();
|
|
361
|
+
for (const release of this.#modeWatches.values()) release();
|
|
362
|
+
this.#modeWatches.clear();
|
|
363
|
+
for (const canvas of this.#canvases.values()) this.#dropLive(canvas);
|
|
364
|
+
this.#parked.clear();
|
|
318
365
|
for (const entry of this.#opening.values()) entry.runtime.dispose();
|
|
319
366
|
this.#opening.clear();
|
|
320
367
|
}
|
|
368
|
+
#agentEnabled(sessionId) {
|
|
369
|
+
return this.#host.minorModes?.active(sessionId).includes(AUTHOR_MODE_ID) === true;
|
|
370
|
+
}
|
|
371
|
+
#watch(sessionId) {
|
|
372
|
+
if (this.#modeWatches.has(sessionId) || this.#host.minorModes === void 0) return;
|
|
373
|
+
const release = this.#host.minorModes.subscribe(sessionId, () => this.#modeChanged(sessionId));
|
|
374
|
+
this.#modeWatches.set(sessionId, release);
|
|
375
|
+
}
|
|
376
|
+
#modeChanged(sessionId) {
|
|
377
|
+
if (this.#disposed) return;
|
|
378
|
+
if (this.#agentEnabled(sessionId)) {
|
|
379
|
+
for (const parked of this.#parked.values()) {
|
|
380
|
+
if (parked.sessionId !== sessionId) continue;
|
|
381
|
+
this.#openLive(parked).catch((error) => console.warn(`Author canvas '${parked.alias}' could not enable browser tools: ${error instanceof Error ? error.message : String(error)}`));
|
|
382
|
+
}
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
for (const [key, entry] of this.#opening) {
|
|
386
|
+
if (entry.parked.sessionId !== sessionId) continue;
|
|
387
|
+
this.#opening.delete(key);
|
|
388
|
+
this.#parked.set(key, entry.parked);
|
|
389
|
+
entry.runtime.dispose();
|
|
390
|
+
}
|
|
391
|
+
for (const canvas of this.#canvases.values()) {
|
|
392
|
+
if (canvas.sessionId !== sessionId) continue;
|
|
393
|
+
this.#parked.set(canvasKey(canvas.sessionId, canvas.alias), {
|
|
394
|
+
sessionId: canvas.sessionId,
|
|
395
|
+
alias: canvas.alias,
|
|
396
|
+
profiles: canvas.documentProfiles
|
|
397
|
+
});
|
|
398
|
+
this.#dropLive(canvas);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
#dropLive(canvas) {
|
|
402
|
+
this.#canvases.delete(canvasKey(canvas.sessionId, canvas.alias));
|
|
403
|
+
this.#release(canvas);
|
|
404
|
+
canvas.runtime.dispose();
|
|
405
|
+
}
|
|
321
406
|
async #replace(canvas, profiles) {
|
|
322
407
|
const key = canvasKey(canvas.sessionId, canvas.alias);
|
|
323
|
-
if (this.#disposed || this.#canvases.get(key) !== canvas) return;
|
|
408
|
+
if (this.#disposed || !this.#agentEnabled(canvas.sessionId) || this.#canvases.get(key) !== canvas) return;
|
|
324
409
|
this.#release(canvas);
|
|
325
410
|
const generation = canvas.generation;
|
|
326
411
|
const replacementId = ++canvas.replacementId;
|
|
@@ -340,7 +425,7 @@ var AuthorBrowserBridge = class {
|
|
|
340
425
|
this.#register(canvas);
|
|
341
426
|
}
|
|
342
427
|
#register(canvas) {
|
|
343
|
-
if (this.#canvases.get(canvasKey(canvas.sessionId, canvas.alias)) === canvas) this.#send(canvas, {
|
|
428
|
+
if (this.#agentEnabled(canvas.sessionId) && this.#canvases.get(canvasKey(canvas.sessionId, canvas.alias)) === canvas) this.#send(canvas, {
|
|
344
429
|
kind: "register",
|
|
345
430
|
alias: canvas.alias,
|
|
346
431
|
generation: canvas.generation
|
|
@@ -384,7 +469,7 @@ var AuthorBrowserBridge = class {
|
|
|
384
469
|
sessionId: canvas.sessionId,
|
|
385
470
|
message: payload
|
|
386
471
|
}
|
|
387
|
-
}).catch(() =>
|
|
472
|
+
}).catch((error) => console.warn(`Author canvas '${canvas.alias}' could not send '${payload.kind}': ${error instanceof Error ? error.message : String(error)}`));
|
|
388
473
|
}
|
|
389
474
|
#current(canvas, requestId, controller) {
|
|
390
475
|
return this.#canvases.get(canvasKey(canvas.sessionId, canvas.alias)) === canvas && canvas.pending.get(requestId) === controller;
|
|
@@ -526,7 +611,8 @@ function resolveAuthorGridCell(sessionId, cell, expectedGeometryToken) {
|
|
|
526
611
|
const TERMINAL_REQUEST_STATUSES = /* @__PURE__ */ new Set([
|
|
527
612
|
"COMPLETE",
|
|
528
613
|
"FAILED",
|
|
529
|
-
"CANCELLED"
|
|
614
|
+
"CANCELLED",
|
|
615
|
+
"UNTRACKED"
|
|
530
616
|
]);
|
|
531
617
|
const EMPTY_SESSION = {
|
|
532
618
|
generation: 0,
|
|
@@ -1133,9 +1219,12 @@ var author_webmcp_web_default = defineChannelFile({
|
|
|
1133
1219
|
}
|
|
1134
1220
|
});
|
|
1135
1221
|
//#endregion
|
|
1222
|
+
//#region src/constants/author.ts
|
|
1223
|
+
const AUTHOR_FILE_TAB_PREFIX = "author-file-";
|
|
1224
|
+
//#endregion
|
|
1136
1225
|
//#region src/extensions/workspaces/sessions/(frontend)/_components/AuthorGridOverlay.tsx
|
|
1137
1226
|
function autonomousVoiceGridVisible(statuses) {
|
|
1138
|
-
return statuses["
|
|
1227
|
+
return statuses["po-voice"]?.trimStart().startsWith("voice auto:") === true;
|
|
1139
1228
|
}
|
|
1140
1229
|
function AuthorGridOverlay({ sessionId, document, visible }) {
|
|
1141
1230
|
const host = useRef(null);
|
|
@@ -1193,9 +1282,9 @@ function AuthorGridOverlay({ sessionId, document, visible }) {
|
|
|
1193
1282
|
const column = index % 8;
|
|
1194
1283
|
const row = Math.floor(index / 8);
|
|
1195
1284
|
return /* @__PURE__ */ jsx("div", {
|
|
1196
|
-
className: "relative border border-
|
|
1285
|
+
className: "relative border border-po-red/25",
|
|
1197
1286
|
children: /* @__PURE__ */ jsxs("span", {
|
|
1198
|
-
className: "absolute left-1 top-0.5 rounded bg-
|
|
1287
|
+
className: "absolute left-1 top-0.5 rounded bg-po-deep/80 px-1 text-2xs font-semibold text-po-red",
|
|
1199
1288
|
children: [AUTHOR_GRID_COLUMNS[column], row + 1]
|
|
1200
1289
|
})
|
|
1201
1290
|
}, index);
|
|
@@ -1308,7 +1397,8 @@ function createAuthorCapturePacket(captureId, capturedAt, document, regions, ali
|
|
|
1308
1397
|
kind: document.kind,
|
|
1309
1398
|
revision: document.version,
|
|
1310
1399
|
...document.sourceSha256 === void 0 ? {} : { sourceSha256: document.sourceSha256 },
|
|
1311
|
-
...document.storyPreview === void 0 ? {} : { storyPreview: structuredClone(document.storyPreview) }
|
|
1400
|
+
...document.storyPreview === void 0 ? {} : { storyPreview: structuredClone(document.storyPreview) },
|
|
1401
|
+
...document.imageArtifact === void 0 ? {} : { imageArtifact: { ...document.imageArtifact } }
|
|
1312
1402
|
},
|
|
1313
1403
|
regions: regions.map((region, index) => {
|
|
1314
1404
|
if (region.documentPath !== document.path || region.revision !== document.version || region.sourceSha256 !== document.sourceSha256) throw new Error(`Region ${String(index + 1)} is stale for the focused document.`);
|
|
@@ -1378,7 +1468,14 @@ function authorCaptureContext(packet) {
|
|
|
1378
1468
|
const metadata = JSON.stringify(packet);
|
|
1379
1469
|
if (bytes(metadata) > 65536) throw new Error("Author capture packet exceeds 64 KiB.");
|
|
1380
1470
|
const annotations = packet.regions.map((region) => `(${String(region.ordinal)}) ${region.comment} [${anchorLocation(region.anchor)}]${region.quote ? `\nQuoted text: ${region.quote}` : ""}`).join("\n\n");
|
|
1381
|
-
const
|
|
1471
|
+
const artifact = packet.document.imageArtifact;
|
|
1472
|
+
const provenance = artifact === void 0 ? storyPreviewProvenance(packet) : [
|
|
1473
|
+
"Generated image feedback: preserve the original image; generate a new refinement only when requested.",
|
|
1474
|
+
`Original artifact: ${artifact.id} (session ${artifact.sessionId})`,
|
|
1475
|
+
`Original canonical path: ${artifact.originalPath}`,
|
|
1476
|
+
`Original SHA-256: ${artifact.sourceSha256}`,
|
|
1477
|
+
`For imagegen refinement use referenced_image_paths: ${JSON.stringify([artifact.originalPath])}`
|
|
1478
|
+
].join("\n");
|
|
1382
1479
|
const content = provenance === void 0 ? annotations : `${provenance}\n\nAnnotations:\n${annotations}`;
|
|
1383
1480
|
return {
|
|
1384
1481
|
kind: "author-capture",
|
|
@@ -1562,11 +1659,11 @@ function AuthorRegionDrafts({ sessionId, path, drafts }) {
|
|
|
1562
1659
|
children: [
|
|
1563
1660
|
drafts.candidate ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1564
1661
|
/* @__PURE__ */ jsxs("h3", {
|
|
1565
|
-
className: "text-base font-semibold text-
|
|
1662
|
+
className: "text-base font-semibold text-po-hi",
|
|
1566
1663
|
children: ["Current ", candidateKind]
|
|
1567
1664
|
}),
|
|
1568
1665
|
/* @__PURE__ */ jsxs("p", {
|
|
1569
|
-
className: "text-base text-
|
|
1666
|
+
className: "text-base text-po-dim sm:text-sm",
|
|
1570
1667
|
children: [
|
|
1571
1668
|
"Add feedback to keep this ",
|
|
1572
1669
|
candidateKind,
|
|
@@ -1574,7 +1671,7 @@ function AuthorRegionDrafts({ sessionId, path, drafts }) {
|
|
|
1574
1671
|
]
|
|
1575
1672
|
}),
|
|
1576
1673
|
drafts.candidate.anchor.kind === "video-time-rect" || drafts.candidate.anchor.kind === "video-time-point" ? /* @__PURE__ */ jsxs("p", {
|
|
1577
|
-
className: "text-base text-
|
|
1674
|
+
className: "text-base text-po-dim sm:text-sm",
|
|
1578
1675
|
children: [
|
|
1579
1676
|
"Frame at ",
|
|
1580
1677
|
drafts.candidate.anchor.timeSeconds.toFixed(3),
|
|
@@ -1587,32 +1684,34 @@ function AuthorRegionDrafts({ sessionId, path, drafts }) {
|
|
|
1587
1684
|
onChange: (event) => setAuthorCandidateText(sessionId, path, event.target.value),
|
|
1588
1685
|
placeholder: "What should change here?",
|
|
1589
1686
|
rows: 3,
|
|
1590
|
-
className: "min-h-
|
|
1591
|
-
}),
|
|
1592
|
-
/* @__PURE__ */ jsx(Button, {
|
|
1593
|
-
className: "min-h-11 min-w-11 text-base [@media(pointer:fine)]:min-h-8 sm:text-sm",
|
|
1594
|
-
variant: "outline",
|
|
1595
|
-
onClick: add,
|
|
1596
|
-
disabled: !drafts.candidate || !drafts.candidateText.trim() || drafts.annotations.length >= 16,
|
|
1597
|
-
children: "add annotation"
|
|
1687
|
+
className: "min-h-24 w-full resize-y rounded-md border border-po-border-soft bg-po-panel p-3 text-lg leading-normal text-po-text placeholder:text-po-dim focus:border-po-green focus:outline-none sm:text-base"
|
|
1598
1688
|
}),
|
|
1599
|
-
/* @__PURE__ */
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1689
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1690
|
+
className: "flex flex-wrap gap-2",
|
|
1691
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
1692
|
+
className: "min-h-11 min-w-11 text-base [@media(pointer:fine)]:min-h-8 sm:text-sm",
|
|
1693
|
+
variant: "outline",
|
|
1694
|
+
onClick: add,
|
|
1695
|
+
disabled: !drafts.candidate || !drafts.candidateText.trim() || drafts.annotations.length >= 16,
|
|
1696
|
+
children: "add annotation"
|
|
1697
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
1698
|
+
variant: "ghost",
|
|
1699
|
+
className: "min-h-11 min-w-11 text-base [@media(pointer:fine)]:min-h-8 sm:text-sm",
|
|
1700
|
+
onClick: () => {
|
|
1701
|
+
setAuthorRegionCandidate(sessionId, path, void 0);
|
|
1702
|
+
setAuthorCandidateText(sessionId, path, "");
|
|
1703
|
+
setError(void 0);
|
|
1704
|
+
},
|
|
1705
|
+
children: "Discard annotation"
|
|
1706
|
+
})]
|
|
1608
1707
|
})
|
|
1609
1708
|
] }) : null,
|
|
1610
1709
|
error ? /* @__PURE__ */ jsx("output", {
|
|
1611
|
-
className: "block text-base text-
|
|
1710
|
+
className: "block text-base text-po-red",
|
|
1612
1711
|
children: error
|
|
1613
1712
|
}) : null,
|
|
1614
1713
|
drafts.annotations.length > 0 ? /* @__PURE__ */ jsxs("h3", {
|
|
1615
|
-
className: "text-
|
|
1714
|
+
className: "text-sm text-po-dim",
|
|
1616
1715
|
children: [
|
|
1617
1716
|
"Unsent annotations (",
|
|
1618
1717
|
drafts.annotations.length,
|
|
@@ -1622,16 +1721,31 @@ function AuthorRegionDrafts({ sessionId, path, drafts }) {
|
|
|
1622
1721
|
/* @__PURE__ */ jsx("ol", {
|
|
1623
1722
|
className: "space-y-2",
|
|
1624
1723
|
children: drafts.annotations.map((region, index) => /* @__PURE__ */ jsxs("li", {
|
|
1625
|
-
className: "rounded border border-
|
|
1724
|
+
className: "flex flex-wrap items-center gap-x-3 gap-y-1 rounded-md border border-po-border-soft bg-po-panel py-1 pl-3 pr-1 text-base leading-normal text-po-text",
|
|
1626
1725
|
children: [
|
|
1627
|
-
/* @__PURE__ */
|
|
1628
|
-
"
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1726
|
+
/* @__PURE__ */ jsx("span", {
|
|
1727
|
+
"aria-hidden": "true",
|
|
1728
|
+
className: "flex h-6 min-w-6 shrink-0 items-center justify-center rounded-full bg-po-magenta px-1 text-sm font-semibold text-po-rail",
|
|
1729
|
+
children: index + 1
|
|
1730
|
+
}),
|
|
1731
|
+
/* @__PURE__ */ jsxs("span", {
|
|
1732
|
+
className: "min-w-0 flex-1 break-words py-1",
|
|
1733
|
+
children: [
|
|
1734
|
+
/* @__PURE__ */ jsxs("span", {
|
|
1735
|
+
className: "sr-only",
|
|
1736
|
+
children: [
|
|
1737
|
+
"(",
|
|
1738
|
+
index + 1,
|
|
1739
|
+
") "
|
|
1740
|
+
]
|
|
1741
|
+
}),
|
|
1742
|
+
region.comment,
|
|
1743
|
+
/* @__PURE__ */ jsx("span", {
|
|
1744
|
+
className: "block text-sm text-po-dim",
|
|
1745
|
+
children: region.mode === "point" ? "Point" : "Region"
|
|
1746
|
+
})
|
|
1747
|
+
]
|
|
1748
|
+
}),
|
|
1635
1749
|
region.anchor.kind === "video-time-rect" || region.anchor.kind === "video-time-point" ? /* @__PURE__ */ jsxs(Button, {
|
|
1636
1750
|
variant: "outline",
|
|
1637
1751
|
className: "min-h-11 min-w-11 text-base [@media(pointer:fine)]:min-h-8 sm:text-sm",
|
|
@@ -1650,7 +1764,10 @@ function AuthorRegionDrafts({ sessionId, path, drafts }) {
|
|
|
1650
1764
|
variant: "ghost",
|
|
1651
1765
|
"aria-label": `Remove annotation ${index + 1}`,
|
|
1652
1766
|
onClick: () => removeAuthorRegion(sessionId, path, region.id),
|
|
1653
|
-
children:
|
|
1767
|
+
children: /* @__PURE__ */ jsx(TrashIcon, {
|
|
1768
|
+
"aria-hidden": "true",
|
|
1769
|
+
className: "size-4"
|
|
1770
|
+
})
|
|
1654
1771
|
})
|
|
1655
1772
|
]
|
|
1656
1773
|
}, region.id))
|
|
@@ -1664,7 +1781,7 @@ function AuthorFeedbackControls({ sessionId, document, drafts, submitCapture })
|
|
|
1664
1781
|
const [status, setStatus] = useState();
|
|
1665
1782
|
const [capturing, setCapturing] = useState(false);
|
|
1666
1783
|
return /* @__PURE__ */ jsxs("div", {
|
|
1667
|
-
className: "space-y-
|
|
1784
|
+
className: "space-y-3",
|
|
1668
1785
|
children: [
|
|
1669
1786
|
/* @__PURE__ */ jsx(AuthorRegionDrafts, {
|
|
1670
1787
|
sessionId,
|
|
@@ -1672,8 +1789,9 @@ function AuthorFeedbackControls({ sessionId, document, drafts, submitCapture })
|
|
|
1672
1789
|
drafts
|
|
1673
1790
|
}),
|
|
1674
1791
|
drafts.annotations.length > 0 ? /* @__PURE__ */ jsx(Button, {
|
|
1675
|
-
className: "min-h-11 min-w-11 w-full text-base
|
|
1676
|
-
|
|
1792
|
+
className: "min-h-11 min-w-11 w-full text-base",
|
|
1793
|
+
size: "lg",
|
|
1794
|
+
variant: "primary",
|
|
1677
1795
|
"data-testid": "author-attach-capture",
|
|
1678
1796
|
disabled: capturing || drafts.candidate !== void 0 || submitCapture === void 0,
|
|
1679
1797
|
onClick: async () => {
|
|
@@ -1696,11 +1814,11 @@ function AuthorFeedbackControls({ sessionId, document, drafts, submitCapture })
|
|
|
1696
1814
|
children: capturing ? "Submitting…" : `Submit ${drafts.annotations.length} annotation${drafts.annotations.length === 1 ? "" : "s"}`
|
|
1697
1815
|
}) : null,
|
|
1698
1816
|
submitCapture === void 0 ? /* @__PURE__ */ jsx("output", {
|
|
1699
|
-
className: "text-sm text-
|
|
1817
|
+
className: "text-sm text-po-red",
|
|
1700
1818
|
children: "Submission unavailable. Reload to update the app."
|
|
1701
1819
|
}) : null,
|
|
1702
1820
|
status ? /* @__PURE__ */ jsx("output", {
|
|
1703
|
-
className: "block text-sm text-
|
|
1821
|
+
className: "block text-sm text-po-dim",
|
|
1704
1822
|
children: status
|
|
1705
1823
|
}) : null
|
|
1706
1824
|
]
|
|
@@ -1714,15 +1832,26 @@ const STATUS_LABELS = {
|
|
|
1714
1832
|
CHANGED: "Change ready",
|
|
1715
1833
|
COMPLETE: "Request completed",
|
|
1716
1834
|
FAILED: "Request failed",
|
|
1717
|
-
CANCELLED: "Request cancelled"
|
|
1835
|
+
CANCELLED: "Request cancelled",
|
|
1836
|
+
UNTRACKED: "Sent, not tracked"
|
|
1718
1837
|
};
|
|
1719
1838
|
const STATUS_STYLES = {
|
|
1720
|
-
REQUESTED: "border-
|
|
1721
|
-
CHANGING: "border-
|
|
1722
|
-
CHANGED: "border-
|
|
1723
|
-
COMPLETE: "border-
|
|
1724
|
-
FAILED: "border-
|
|
1725
|
-
CANCELLED: "border-
|
|
1839
|
+
REQUESTED: "border-po-border-soft bg-po-bg text-po-dim",
|
|
1840
|
+
CHANGING: "border-po-edge-green bg-po-tint-green text-po-green",
|
|
1841
|
+
CHANGED: "border-po-edge-green bg-po-tint-green text-po-green",
|
|
1842
|
+
COMPLETE: "border-po-edge-green bg-po-tint-green text-po-green",
|
|
1843
|
+
FAILED: "border-po-edge-red bg-po-tint-red text-po-red",
|
|
1844
|
+
CANCELLED: "border-po-border-soft bg-po-bg text-po-dim",
|
|
1845
|
+
UNTRACKED: "border-po-border-soft bg-po-bg text-po-dim"
|
|
1846
|
+
};
|
|
1847
|
+
const STATUS_TONES = {
|
|
1848
|
+
REQUESTED: "neutral",
|
|
1849
|
+
CHANGING: "ok",
|
|
1850
|
+
CHANGED: "ok",
|
|
1851
|
+
COMPLETE: "ok",
|
|
1852
|
+
FAILED: "error",
|
|
1853
|
+
CANCELLED: "neutral",
|
|
1854
|
+
UNTRACKED: "neutral"
|
|
1726
1855
|
};
|
|
1727
1856
|
function requestInstruction(requestText) {
|
|
1728
1857
|
const referencedContext = requestText.search(/(?:^|\s+)Referenced context "/u);
|
|
@@ -1755,38 +1884,49 @@ function progressLabel(request) {
|
|
|
1755
1884
|
if (request.status === "COMPLETE") return "Agent finished processing this request";
|
|
1756
1885
|
if (request.status === "FAILED") return "Stopped with an error";
|
|
1757
1886
|
if (request.status === "CANCELLED") return "Stopped before completion";
|
|
1887
|
+
if (request.status === "UNTRACKED") return "Sent to the agent · see its session for the outcome";
|
|
1758
1888
|
return `${total} annotation${total === 1 ? "" : "s"} queued`;
|
|
1759
1889
|
}
|
|
1760
1890
|
function RequestRecord({ request, prominent = false }) {
|
|
1761
1891
|
const instruction = requestInstruction(request.requestText);
|
|
1762
1892
|
return /* @__PURE__ */ jsxs("article", {
|
|
1763
1893
|
"data-testid": prominent ? "author-active-request" : void 0,
|
|
1764
|
-
className: "space-y-3",
|
|
1894
|
+
className: "space-y-3 rounded-md border border-po-border-soft bg-po-panel p-3",
|
|
1765
1895
|
children: [
|
|
1766
1896
|
/* @__PURE__ */ jsxs("header", {
|
|
1767
|
-
className: "border-b border-
|
|
1768
|
-
children: [/* @__PURE__ */ jsx(
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
}), /* @__PURE__ */ jsxs("
|
|
1772
|
-
className: "
|
|
1773
|
-
children: [
|
|
1774
|
-
"
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1897
|
+
className: "flex gap-3 border-b border-po-border-soft pb-3",
|
|
1898
|
+
children: [/* @__PURE__ */ jsx(SparklesIcon, {
|
|
1899
|
+
"aria-hidden": "true",
|
|
1900
|
+
className: "mt-1 size-4 shrink-0 text-po-green"
|
|
1901
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1902
|
+
className: "min-w-0 flex-1",
|
|
1903
|
+
children: [
|
|
1904
|
+
/* @__PURE__ */ jsx("p", {
|
|
1905
|
+
className: "text-lg font-semibold text-po-hi",
|
|
1906
|
+
children: "Document context"
|
|
1907
|
+
}),
|
|
1908
|
+
/* @__PURE__ */ jsx("p", {
|
|
1909
|
+
className: "mt-1 truncate text-base font-semibold text-po-hi",
|
|
1910
|
+
children: documentName(request.documentPath)
|
|
1911
|
+
}),
|
|
1912
|
+
/* @__PURE__ */ jsx(StatusBadge, {
|
|
1913
|
+
tone: STATUS_TONES[request.status],
|
|
1914
|
+
className: "mt-2 whitespace-normal",
|
|
1915
|
+
children: progressLabel(request)
|
|
1916
|
+
})
|
|
1917
|
+
]
|
|
1778
1918
|
})]
|
|
1779
1919
|
}),
|
|
1780
1920
|
/* @__PURE__ */ jsx("section", {
|
|
1781
1921
|
"aria-label": "Request locations",
|
|
1782
|
-
className: "rounded border border-
|
|
1922
|
+
className: "rounded-md border border-po-border-soft bg-po-bg p-3",
|
|
1783
1923
|
children: /* @__PURE__ */ jsx("ol", {
|
|
1784
1924
|
className: "space-y-3",
|
|
1785
1925
|
children: request.regions.map((region, index) => /* @__PURE__ */ jsxs("li", {
|
|
1786
1926
|
className: "space-y-2",
|
|
1787
1927
|
children: [
|
|
1788
1928
|
/* @__PURE__ */ jsxs("p", {
|
|
1789
|
-
className: "text-sm font-medium text-
|
|
1929
|
+
className: "text-sm font-medium text-po-magenta",
|
|
1790
1930
|
children: [
|
|
1791
1931
|
"Where · ",
|
|
1792
1932
|
request.regions.length > 1 ? `${index + 1} · ` : "",
|
|
@@ -1798,7 +1938,7 @@ function RequestRecord({ request, prominent = false }) {
|
|
|
1798
1938
|
]
|
|
1799
1939
|
}),
|
|
1800
1940
|
region.quote ? /* @__PURE__ */ jsxs("blockquote", {
|
|
1801
|
-
className: "line-clamp-3 text-base italic leading-relaxed text-
|
|
1941
|
+
className: "line-clamp-3 text-base italic leading-relaxed text-po-text",
|
|
1802
1942
|
children: [
|
|
1803
1943
|
"“",
|
|
1804
1944
|
region.quote,
|
|
@@ -1806,7 +1946,7 @@ function RequestRecord({ request, prominent = false }) {
|
|
|
1806
1946
|
]
|
|
1807
1947
|
}) : null,
|
|
1808
1948
|
/* @__PURE__ */ jsx("p", {
|
|
1809
|
-
className: "text-base leading-relaxed text-
|
|
1949
|
+
className: "text-base leading-relaxed text-po-dim",
|
|
1810
1950
|
children: region.comment
|
|
1811
1951
|
})
|
|
1812
1952
|
]
|
|
@@ -1815,7 +1955,7 @@ function RequestRecord({ request, prominent = false }) {
|
|
|
1815
1955
|
}),
|
|
1816
1956
|
/* @__PURE__ */ jsxs("section", {
|
|
1817
1957
|
"data-testid": prominent ? "author-operation-card" : void 0,
|
|
1818
|
-
className: `rounded border p-3 ${STATUS_STYLES[request.status]}`,
|
|
1958
|
+
className: `rounded-md border p-3 ${STATUS_STYLES[request.status]}`,
|
|
1819
1959
|
children: [
|
|
1820
1960
|
/* @__PURE__ */ jsxs("p", {
|
|
1821
1961
|
className: "text-sm font-semibold",
|
|
@@ -1826,15 +1966,15 @@ function RequestRecord({ request, prominent = false }) {
|
|
|
1826
1966
|
]
|
|
1827
1967
|
}),
|
|
1828
1968
|
/* @__PURE__ */ jsx("p", {
|
|
1829
|
-
className: "mt-2 text-base leading-relaxed text-
|
|
1969
|
+
className: "mt-2 text-base leading-relaxed text-po-hi",
|
|
1830
1970
|
children: request.currentOperation || STATUS_LABELS[request.status]
|
|
1831
1971
|
}),
|
|
1832
1972
|
request.error ? /* @__PURE__ */ jsx("p", {
|
|
1833
|
-
className: "mt-2 text-sm leading-relaxed text-
|
|
1973
|
+
className: "mt-2 text-sm leading-relaxed text-po-red",
|
|
1834
1974
|
children: request.error
|
|
1835
1975
|
}) : null,
|
|
1836
1976
|
request.before !== void 0 || request.after !== void 0 ? /* @__PURE__ */ jsxs("details", {
|
|
1837
|
-
className: "mt-2 border-t border-current/20 pt-2 text-base text-
|
|
1977
|
+
className: "mt-2 border-t border-current/20 pt-2 text-base text-po-dim",
|
|
1838
1978
|
children: [
|
|
1839
1979
|
/* @__PURE__ */ jsx("summary", {
|
|
1840
1980
|
className: "cursor-pointer select-none",
|
|
@@ -1855,19 +1995,15 @@ function RequestRecord({ request, prominent = false }) {
|
|
|
1855
1995
|
/* @__PURE__ */ jsxs("section", {
|
|
1856
1996
|
className: "space-y-2",
|
|
1857
1997
|
children: [/* @__PURE__ */ jsx("p", {
|
|
1858
|
-
className: "text-
|
|
1998
|
+
className: "text-base font-semibold text-po-hi",
|
|
1859
1999
|
children: "Request · how to change"
|
|
1860
2000
|
}), /* @__PURE__ */ jsx("div", {
|
|
1861
|
-
className: "rounded border border-
|
|
2001
|
+
className: "rounded-md border border-po-border-soft bg-po-bg p-3",
|
|
1862
2002
|
children: /* @__PURE__ */ jsx("p", {
|
|
1863
|
-
className: "whitespace-pre-wrap text-base leading-relaxed text-
|
|
2003
|
+
className: "whitespace-pre-wrap text-base leading-relaxed text-po-text",
|
|
1864
2004
|
children: instruction || "No additional instruction."
|
|
1865
2005
|
})
|
|
1866
2006
|
})]
|
|
1867
|
-
}),
|
|
1868
|
-
/* @__PURE__ */ jsx("footer", {
|
|
1869
|
-
className: "text-sm text-doom-dim",
|
|
1870
|
-
children: progressLabel(request)
|
|
1871
2007
|
})
|
|
1872
2008
|
]
|
|
1873
2009
|
});
|
|
@@ -1880,21 +2016,21 @@ function AuthorRequestLog({ requests }) {
|
|
|
1880
2016
|
className: "space-y-3",
|
|
1881
2017
|
children: [
|
|
1882
2018
|
/* @__PURE__ */ jsx("h3", {
|
|
1883
|
-
className: "text-lg font-semibold text-
|
|
1884
|
-
children: "
|
|
2019
|
+
className: "text-lg font-semibold text-po-hi",
|
|
2020
|
+
children: "Author"
|
|
1885
2021
|
}),
|
|
1886
2022
|
latest === void 0 ? /* @__PURE__ */ jsx("p", {
|
|
1887
|
-
className: "text-sm leading-relaxed text-
|
|
2023
|
+
className: "text-sm leading-relaxed text-po-dim",
|
|
1888
2024
|
children: "No requests or changes yet."
|
|
1889
2025
|
}) : /* @__PURE__ */ jsx(RequestRecord, {
|
|
1890
2026
|
request: latest,
|
|
1891
2027
|
prominent: true
|
|
1892
2028
|
}),
|
|
1893
2029
|
earlier.length > 0 ? /* @__PURE__ */ jsxs("details", {
|
|
1894
|
-
className: "border-t border-
|
|
2030
|
+
className: "border-t border-po-border-soft pt-3",
|
|
1895
2031
|
"data-testid": "author-earlier-requests",
|
|
1896
2032
|
children: [/* @__PURE__ */ jsxs("summary", {
|
|
1897
|
-
className: "cursor-pointer select-none text-base font-medium text-
|
|
2033
|
+
className: "cursor-pointer select-none text-base font-medium text-po-dim",
|
|
1898
2034
|
children: [
|
|
1899
2035
|
"Earlier requests (",
|
|
1900
2036
|
earlier.length,
|
|
@@ -1910,6 +2046,10 @@ function AuthorRequestLog({ requests }) {
|
|
|
1910
2046
|
}
|
|
1911
2047
|
//#endregion
|
|
1912
2048
|
//#region src/extensions/workspaces/sessions/(frontend)/dock/_components/AuthorToolPalette.tsx
|
|
2049
|
+
/** One tool tile. An active annotation tool is the person's mark (rose); an active view tool is Po's jade. */
|
|
2050
|
+
function authorToolClassName(active, annotates) {
|
|
2051
|
+
return `h-auto min-h-11 min-w-11 flex-col gap-1 rounded-md px-1 py-2 text-base [@media(pointer:fine)]:min-h-8 ${!active ? "border-po-border-soft bg-po-panel text-po-dim" : annotates ? "border-po-edge-magenta bg-po-tint-magenta font-semibold text-po-magenta hover:text-po-magenta" : "border-po-edge-green bg-po-tint-green font-semibold text-po-green hover:text-po-green"}`;
|
|
2052
|
+
}
|
|
1913
2053
|
function AuthorToolPalette({ sessionId, path, kind, activeTool }) {
|
|
1914
2054
|
const [error, setError] = useState();
|
|
1915
2055
|
const format = (style) => {
|
|
@@ -1937,18 +2077,21 @@ function AuthorToolPalette({ sessionId, path, kind, activeTool }) {
|
|
|
1937
2077
|
label: "Pan",
|
|
1938
2078
|
glyph: "✋",
|
|
1939
2079
|
action: () => setAuthorToolMode(sessionId, path, activeTool === "pan" ? "select" : "pan"),
|
|
1940
|
-
active: activeTool === "pan"
|
|
2080
|
+
active: activeTool === "pan",
|
|
2081
|
+
annotates: false
|
|
1941
2082
|
}, {
|
|
1942
2083
|
label: "Draw",
|
|
1943
2084
|
glyph: "✎",
|
|
1944
2085
|
action: () => setAuthorToolMode(sessionId, path, activeTool === "draw" ? "select" : "draw"),
|
|
1945
|
-
active: activeTool === "draw"
|
|
2086
|
+
active: activeTool === "draw",
|
|
2087
|
+
annotates: true
|
|
1946
2088
|
}] : [],
|
|
1947
2089
|
{
|
|
1948
2090
|
label: "Region",
|
|
1949
2091
|
glyph: "⌗",
|
|
1950
2092
|
action: () => setAuthorToolMode(sessionId, path, activeTool === "mark" ? "select" : "mark"),
|
|
1951
|
-
active: activeTool === "mark"
|
|
2093
|
+
active: activeTool === "mark",
|
|
2094
|
+
annotates: true
|
|
1952
2095
|
},
|
|
1953
2096
|
...kind === "markdown" ? [
|
|
1954
2097
|
{
|
|
@@ -1976,41 +2119,43 @@ function AuthorToolPalette({ sessionId, path, kind, activeTool }) {
|
|
|
1976
2119
|
label: "Comment",
|
|
1977
2120
|
glyph: "●",
|
|
1978
2121
|
action: () => setAuthorToolMode(sessionId, path, activeTool === "comment" ? "select" : "comment"),
|
|
1979
|
-
active: activeTool === "comment"
|
|
2122
|
+
active: activeTool === "comment",
|
|
2123
|
+
annotates: true
|
|
1980
2124
|
}
|
|
1981
2125
|
];
|
|
1982
2126
|
return /* @__PURE__ */ jsxs("section", {
|
|
1983
2127
|
"data-testid": "author-tool-palette",
|
|
1984
|
-
className: "space-y-2
|
|
2128
|
+
className: "space-y-2",
|
|
1985
2129
|
children: [
|
|
1986
|
-
/* @__PURE__ */
|
|
1987
|
-
className: "
|
|
1988
|
-
children:
|
|
1989
|
-
className: "text-lg font-semibold text-doom-hi",
|
|
1990
|
-
children: "Tools"
|
|
1991
|
-
}), /* @__PURE__ */ jsx("span", { children: "focused tab" })]
|
|
2130
|
+
/* @__PURE__ */ jsx("h3", {
|
|
2131
|
+
className: "sr-only",
|
|
2132
|
+
children: "Tools"
|
|
1992
2133
|
}),
|
|
1993
2134
|
/* @__PURE__ */ jsx("div", {
|
|
1994
|
-
className: "grid grid-cols-
|
|
2135
|
+
className: "grid grid-cols-4 gap-1.5",
|
|
1995
2136
|
children: tools.map((tool) => /* @__PURE__ */ jsxs(Button, {
|
|
1996
2137
|
size: "md",
|
|
1997
2138
|
variant: "outline",
|
|
1998
2139
|
"aria-label": tool.label === "Region" ? "mark region" : tool.label,
|
|
1999
2140
|
"aria-pressed": tool.active,
|
|
2000
2141
|
onClick: tool.action,
|
|
2001
|
-
className:
|
|
2142
|
+
className: authorToolClassName(tool.active === true, tool.annotates === true),
|
|
2002
2143
|
children: [/* @__PURE__ */ jsx("span", {
|
|
2003
2144
|
"aria-hidden": "true",
|
|
2145
|
+
className: "text-lg leading-none",
|
|
2004
2146
|
children: tool.glyph
|
|
2005
|
-
}),
|
|
2147
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2148
|
+
className: "text-sm",
|
|
2149
|
+
children: tool.label
|
|
2150
|
+
})]
|
|
2006
2151
|
}, tool.label))
|
|
2007
2152
|
}),
|
|
2008
2153
|
/* @__PURE__ */ jsx("p", {
|
|
2009
|
-
className: "text-base leading-normal text-
|
|
2154
|
+
className: "text-base leading-normal text-po-dim sm:text-sm",
|
|
2010
2155
|
children: activeTool === "draw" ? "Draw a location mark, then enter feedback text. Marks never edit the source." : activeTool === "pan" ? "Drag to pan; pinch to zoom. Use Fit to reset the view." : activeTool === "mark" ? "Drag over the document to mark a region." : activeTool === "comment" ? "Click the document to place a comment pin." : "Choose Region to drag an area, or Comment to place a point."
|
|
2011
2156
|
}),
|
|
2012
2157
|
error ? /* @__PURE__ */ jsx("output", {
|
|
2013
|
-
className: "block text-base text-
|
|
2158
|
+
className: "block text-base text-po-red",
|
|
2014
2159
|
children: error
|
|
2015
2160
|
}) : null
|
|
2016
2161
|
]
|
|
@@ -2018,7 +2163,7 @@ function AuthorToolPalette({ sessionId, path, kind, activeTool }) {
|
|
|
2018
2163
|
}
|
|
2019
2164
|
//#endregion
|
|
2020
2165
|
//#region src/extensions/workspaces/sessions/(frontend)/dock/_components/AuthorPanel.tsx
|
|
2021
|
-
function AuthorPanel({ sessionId,
|
|
2166
|
+
function AuthorPanel({ sessionId, submitCapture, statuses, renderSlot, renderSessionActivity }) {
|
|
2022
2167
|
const documents = useStore(authorWorkspace.store, (state) => {
|
|
2023
2168
|
if (sessionId === null) return [];
|
|
2024
2169
|
const prefix = `${sessionId}\n`;
|
|
@@ -2031,43 +2176,43 @@ function AuthorPanel({ sessionId, activeMinorModes, submitCapture, statuses, ren
|
|
|
2031
2176
|
const requests = (workspace?.requests ?? []).filter((request) => focusedPath === void 0 || request.documentPath === focusedPath);
|
|
2032
2177
|
const drafts = useStore(authorWorkspace.store, (state) => authorDocumentAnnotations(sessionId, focusedPath ?? "", state));
|
|
2033
2178
|
const activity = sessionId !== null && focusedPath !== void 0 && requests.some((request) => request.status === "REQUESTED" || request.status === "CHANGING") ? renderSessionActivity?.() ?? null : null;
|
|
2034
|
-
if (!activeMinorModes?.includes("author")) return null;
|
|
2035
2179
|
return /* @__PURE__ */ jsxs("section", {
|
|
2036
2180
|
"data-testid": "author-panel",
|
|
2037
2181
|
className: "flex min-h-0 flex-1 flex-col overflow-hidden",
|
|
2038
2182
|
children: [/* @__PURE__ */ jsx("div", {
|
|
2039
2183
|
className: "min-h-0 flex-1 overflow-y-auto p-3",
|
|
2040
2184
|
children: /* @__PURE__ */ jsxs("div", {
|
|
2041
|
-
className: "flex flex-col gap-
|
|
2185
|
+
className: "flex flex-col gap-3",
|
|
2042
2186
|
children: [
|
|
2043
2187
|
/* @__PURE__ */ jsx("div", {
|
|
2044
2188
|
"data-testid": "author-preview-providers",
|
|
2189
|
+
className: "empty:hidden",
|
|
2045
2190
|
children: renderSlot("author.preview-provider")
|
|
2046
2191
|
}),
|
|
2047
2192
|
sessionId !== null && workspace !== void 0 && focused !== void 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2193
|
+
/* @__PURE__ */ jsx(AuthorToolPalette, {
|
|
2194
|
+
sessionId,
|
|
2195
|
+
path: focused.path,
|
|
2196
|
+
kind: focused.kind,
|
|
2197
|
+
activeTool: workspace.toolsByDocument[normalizeAuthorPath(focused.path)] ?? "select"
|
|
2198
|
+
}),
|
|
2048
2199
|
/* @__PURE__ */ jsx("h2", {
|
|
2049
|
-
className: "text-
|
|
2200
|
+
className: "border-t border-po-border-soft pt-3 text-lg font-semibold text-po-hi",
|
|
2050
2201
|
children: "Annotations"
|
|
2051
2202
|
}),
|
|
2052
2203
|
/* @__PURE__ */ jsx("p", {
|
|
2053
|
-
className: "text-base leading-normal text-
|
|
2204
|
+
className: "text-base leading-normal text-po-dim sm:text-sm",
|
|
2054
2205
|
children: "Drag a region or place a point, add feedback, then submit here. Requests queue while the agent is working."
|
|
2055
2206
|
}),
|
|
2056
2207
|
focused.kind === "video" ? /* @__PURE__ */ jsx("p", {
|
|
2057
|
-
className: "text-base text-
|
|
2208
|
+
className: "text-base text-po-dim sm:text-sm",
|
|
2058
2209
|
children: "Annotations reference video frames. They do not save changes to the source video."
|
|
2059
2210
|
}) : null,
|
|
2060
|
-
/* @__PURE__ */ jsx(AuthorToolPalette, {
|
|
2061
|
-
sessionId,
|
|
2062
|
-
path: focused.path,
|
|
2063
|
-
kind: focused.kind,
|
|
2064
|
-
activeTool: workspace.toolsByDocument[normalizeAuthorPath(focused.path)] ?? "select"
|
|
2065
|
-
}),
|
|
2066
2211
|
autonomousVoiceGridVisible(statuses) && grid !== void 0 ? /* @__PURE__ */ jsxs("div", {
|
|
2067
2212
|
"data-testid": "author-grid-snapshot",
|
|
2068
|
-
className: "rounded border border-
|
|
2213
|
+
className: "rounded border border-po-red/40 bg-po-red/5 p-2 text-base text-po-dim",
|
|
2069
2214
|
children: [/* @__PURE__ */ jsx("strong", {
|
|
2070
|
-
className: "text-
|
|
2215
|
+
className: "text-po-red",
|
|
2071
2216
|
children: "VOICE GRID A1–H8"
|
|
2072
2217
|
}), /* @__PURE__ */ jsxs("p", {
|
|
2073
2218
|
className: "mt-1 truncate",
|
|
@@ -2075,7 +2220,7 @@ function AuthorPanel({ sessionId, activeMinorModes, submitCapture, statuses, ren
|
|
|
2075
2220
|
})]
|
|
2076
2221
|
}) : null,
|
|
2077
2222
|
/* @__PURE__ */ jsx("div", {
|
|
2078
|
-
className: "
|
|
2223
|
+
className: "max-sm:hidden",
|
|
2079
2224
|
children: /* @__PURE__ */ jsx(AuthorFeedbackControls, {
|
|
2080
2225
|
sessionId,
|
|
2081
2226
|
document: focused,
|
|
@@ -2084,12 +2229,15 @@ function AuthorPanel({ sessionId, activeMinorModes, submitCapture, statuses, ren
|
|
|
2084
2229
|
}, focused.path)
|
|
2085
2230
|
})
|
|
2086
2231
|
] }) : null,
|
|
2087
|
-
/* @__PURE__ */ jsx(
|
|
2232
|
+
/* @__PURE__ */ jsx("div", {
|
|
2233
|
+
className: focused === void 0 ? void 0 : "border-t border-po-border-soft pt-3",
|
|
2234
|
+
children: /* @__PURE__ */ jsx(AuthorRequestLog, { requests })
|
|
2235
|
+
})
|
|
2088
2236
|
]
|
|
2089
2237
|
})
|
|
2090
2238
|
}), activity === null ? null : /* @__PURE__ */ jsx("footer", {
|
|
2091
2239
|
"data-testid": "author-session-activity",
|
|
2092
|
-
className: "shrink-0 border-t border-
|
|
2240
|
+
className: "shrink-0 border-t border-po-border bg-po-rail p-3",
|
|
2093
2241
|
children: activity
|
|
2094
2242
|
})]
|
|
2095
2243
|
});
|
|
@@ -2098,17 +2246,17 @@ function AuthorPanel({ sessionId, activeMinorModes, submitCapture, statuses, ren
|
|
|
2098
2246
|
//#region src/extensions/workspaces/sessions/(frontend)/dock/authoring.web.tsx
|
|
2099
2247
|
var authoring_web_default = defineDockFace({
|
|
2100
2248
|
id: "authoring",
|
|
2101
|
-
label: "
|
|
2249
|
+
label: "Author",
|
|
2102
2250
|
order: 30,
|
|
2103
|
-
autoSelect: true,
|
|
2251
|
+
autoSelect: ({ tabId }) => tabId?.startsWith(AUTHOR_FILE_TAB_PREFIX) === true,
|
|
2104
2252
|
panel: AuthorPanel,
|
|
2105
|
-
requiredMinorMode: "author",
|
|
2106
2253
|
visibility: {
|
|
2107
|
-
subscribe() {
|
|
2108
|
-
|
|
2254
|
+
subscribe(listener) {
|
|
2255
|
+
const subscription = authorWorkspace.store.subscribe(listener);
|
|
2256
|
+
return () => subscription.unsubscribe();
|
|
2109
2257
|
},
|
|
2110
2258
|
isVisible(sessionId) {
|
|
2111
|
-
return sessionId !== null;
|
|
2259
|
+
return sessionId !== null && authorWorkspace.store.state.sessions[sessionId]?.focusedDocument !== void 0;
|
|
2112
2260
|
}
|
|
2113
2261
|
}
|
|
2114
2262
|
});
|
|
@@ -2307,8 +2455,29 @@ function authorKindForPath(path) {
|
|
|
2307
2455
|
* itself, and the digest travels in a header on both legs.
|
|
2308
2456
|
*/
|
|
2309
2457
|
function authorSessionFileUrl(sessionId, path) {
|
|
2458
|
+
if (path.startsWith("artifact:")) return artifactApi.session(sessionId).content.url({ query: { path: path.slice(9) } });
|
|
2310
2459
|
return api.session(sessionId).file.url({ query: { path } });
|
|
2311
2460
|
}
|
|
2461
|
+
/** The file name a person sees: the last path segment, without the `artifact:` scheme. */
|
|
2462
|
+
function authorFileName(path) {
|
|
2463
|
+
const local = path.startsWith("artifact:") ? path.slice(9) : path;
|
|
2464
|
+
return local.split("/").at(-1) ?? local;
|
|
2465
|
+
}
|
|
2466
|
+
/**
|
|
2467
|
+
* po-artifact's read route, which owns the session artifact folder's guards.
|
|
2468
|
+
* Browser code cannot import that package, so its one raw route is restated.
|
|
2469
|
+
*/
|
|
2470
|
+
const artifactApi = createApiClient({ content: {
|
|
2471
|
+
method: "GET",
|
|
2472
|
+
path: "/content",
|
|
2473
|
+
query: ["path"],
|
|
2474
|
+
raw: true
|
|
2475
|
+
} }, {
|
|
2476
|
+
scopes: ["session"],
|
|
2477
|
+
basePath: "artifacts",
|
|
2478
|
+
transport: (input, init) => sealedTransport.fetch(input, init),
|
|
2479
|
+
sessionAddress: sessionApiAddress
|
|
2480
|
+
});
|
|
2312
2481
|
/** One document route, with the refusal the reader is shown: the route's own words, or its status. */
|
|
2313
2482
|
async function documentRequest(method, sessionId, body, signal) {
|
|
2314
2483
|
const result = await method({
|
|
@@ -2338,7 +2507,7 @@ async function loadAuthorDocument(sessionId, path, signal) {
|
|
|
2338
2507
|
return {
|
|
2339
2508
|
path,
|
|
2340
2509
|
kind,
|
|
2341
|
-
title: path
|
|
2510
|
+
title: authorFileName(path),
|
|
2342
2511
|
structuredFormat: format,
|
|
2343
2512
|
fragments: parsed.fragments,
|
|
2344
2513
|
...csvDialect === void 0 ? {} : { csvDialect },
|
|
@@ -2351,7 +2520,7 @@ async function loadAuthorDocument(sessionId, path, signal) {
|
|
|
2351
2520
|
return {
|
|
2352
2521
|
path,
|
|
2353
2522
|
kind,
|
|
2354
|
-
title: path
|
|
2523
|
+
title: authorFileName(path),
|
|
2355
2524
|
...content === void 0 ? {} : { content },
|
|
2356
2525
|
...mediaUrl === void 0 ? {} : { mediaUrl },
|
|
2357
2526
|
...sourceSha256 === void 0 ? {} : { sourceSha256 }
|
|
@@ -2427,6 +2596,7 @@ async function bytesToSave(sessionId, document, signal) {
|
|
|
2427
2596
|
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
2428
2597
|
}
|
|
2429
2598
|
async function saveAuthorDocument(sessionId, document, signal) {
|
|
2599
|
+
if (document.path.startsWith("artifact:")) throw new Error("Artifacts are read-only.");
|
|
2430
2600
|
if (document.sourceSha256 === void 0) throw new Error("This Author view has no source digest.");
|
|
2431
2601
|
const response = await sealedTransport.fetch(authorSessionFileUrl(sessionId, document.path), {
|
|
2432
2602
|
method: "PUT",
|
|
@@ -2865,13 +3035,17 @@ function authorProfilesForDocument(sessionId, path, kind) {
|
|
|
2865
3035
|
* own. The transport stays raw because what comes back is a blob, and a call
|
|
2866
3036
|
* through the generated client reads the body as JSON to report a refusal.
|
|
2867
3037
|
*/
|
|
2868
|
-
async function loadAuthorMedia(source, signal) {
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
3038
|
+
async function loadAuthorMedia(source, signal, imageData) {
|
|
3039
|
+
let blob;
|
|
3040
|
+
if (imageData !== void 0) blob = new Blob([Uint8Array.from(atob(imageData), (character) => character.charCodeAt(0))], { type: "image/png" });
|
|
3041
|
+
else {
|
|
3042
|
+
const response = await sealedTransport.fetch(source, {
|
|
3043
|
+
signal,
|
|
3044
|
+
cache: "no-store"
|
|
3045
|
+
});
|
|
3046
|
+
if (!response.ok) throw new Error(`Author media could not be loaded (${response.status}).`);
|
|
3047
|
+
blob = await response.blob();
|
|
3048
|
+
}
|
|
2875
3049
|
signal.throwIfAborted();
|
|
2876
3050
|
const url = URL.createObjectURL(blob);
|
|
2877
3051
|
let disposed = false;
|
|
@@ -2897,7 +3071,7 @@ function AuthorVideoControls({ playback, ready, locked, marking, onSeek, onToggl
|
|
|
2897
3071
|
const buttonClass = "min-h-11 px-3 text-base [@media(pointer:fine)]:min-h-8 sm:text-sm";
|
|
2898
3072
|
return /* @__PURE__ */ jsxs("section", {
|
|
2899
3073
|
"aria-label": "Video annotation controls",
|
|
2900
|
-
className: "mt-2 space-y-2 rounded border border-
|
|
3074
|
+
className: "mt-2 space-y-2 rounded border border-po-border bg-po-panel p-2.5 text-base text-po-text sm:text-sm",
|
|
2901
3075
|
children: [
|
|
2902
3076
|
/* @__PURE__ */ jsxs("div", {
|
|
2903
3077
|
className: "flex flex-wrap items-center justify-between gap-3",
|
|
@@ -2910,7 +3084,7 @@ function AuthorVideoControls({ playback, ready, locked, marking, onSeek, onToggl
|
|
|
2910
3084
|
videoTimeLabel(duration)
|
|
2911
3085
|
]
|
|
2912
3086
|
}), /* @__PURE__ */ jsx("span", {
|
|
2913
|
-
className: "text-
|
|
3087
|
+
className: "text-po-dim",
|
|
2914
3088
|
children: locked ? "Annotation draft: add or discard it before seeking." : marking ? "Frame paused. Drag a region on the video." : "Seek to a moment, then choose Annotate this frame."
|
|
2915
3089
|
})]
|
|
2916
3090
|
}),
|
|
@@ -2923,7 +3097,7 @@ function AuthorVideoControls({ playback, ready, locked, marking, onSeek, onToggl
|
|
|
2923
3097
|
value: Math.min(playback.currentTime, duration),
|
|
2924
3098
|
disabled: locked || duration <= 0,
|
|
2925
3099
|
onChange: (event) => onSeek(Number(event.target.value)),
|
|
2926
|
-
className: "h-11 w-full cursor-pointer accent-
|
|
3100
|
+
className: "h-11 w-full cursor-pointer accent-po-blue [@media(pointer:fine)]:h-6"
|
|
2927
3101
|
}),
|
|
2928
3102
|
/* @__PURE__ */ jsxs("div", {
|
|
2929
3103
|
className: "flex flex-wrap items-center gap-2",
|
|
@@ -2964,7 +3138,7 @@ function AuthorVideoControls({ playback, ready, locked, marking, onSeek, onToggl
|
|
|
2964
3138
|
children: [
|
|
2965
3139
|
/* @__PURE__ */ jsx("label", {
|
|
2966
3140
|
htmlFor: "author-video-seconds",
|
|
2967
|
-
className: "text-
|
|
3141
|
+
className: "text-po-dim",
|
|
2968
3142
|
children: "Go to seconds"
|
|
2969
3143
|
}),
|
|
2970
3144
|
/* @__PURE__ */ jsx("input", {
|
|
@@ -2977,7 +3151,7 @@ function AuthorVideoControls({ playback, ready, locked, marking, onSeek, onToggl
|
|
|
2977
3151
|
onChange: (event) => setSeconds(event.target.value),
|
|
2978
3152
|
disabled: locked || duration <= 0,
|
|
2979
3153
|
placeholder: "1.250",
|
|
2980
|
-
className: "h-11 w-24 rounded border border-
|
|
3154
|
+
className: "h-11 w-24 rounded border border-po-border bg-po-deep px-2 text-lg [@media(pointer:fine)]:h-8 sm:text-sm"
|
|
2981
3155
|
}),
|
|
2982
3156
|
/* @__PURE__ */ jsx(Button, {
|
|
2983
3157
|
type: "submit",
|
|
@@ -2994,6 +3168,8 @@ function AuthorVideoControls({ playback, ready, locked, marking, onSeek, onToggl
|
|
|
2994
3168
|
}
|
|
2995
3169
|
//#endregion
|
|
2996
3170
|
//#region src/extensions/workspaces/sessions/(frontend)/_components/AuthorMediaView.tsx
|
|
3171
|
+
const REGION_FRAME = "pointer-events-none absolute border-2 border-po-magenta bg-po-magenta/10";
|
|
3172
|
+
const ORDINAL_BADGE = "absolute -left-3 -top-3 flex h-6 min-w-6 items-center justify-center rounded-full border-2 border-po-panel bg-po-magenta px-1 text-sm font-semibold text-po-rail";
|
|
2997
3173
|
/** Marks source media. Native playback and page controls remain usable in select mode. */
|
|
2998
3174
|
function AuthorMediaView({ sessionId, document: source, activeTool, displayedRegions, pendingCandidate = false, seekRequest }) {
|
|
2999
3175
|
const host = useRef(null);
|
|
@@ -3004,7 +3180,35 @@ function AuthorMediaView({ sessionId, document: source, activeTool, displayedReg
|
|
|
3004
3180
|
const pointers = useRef(/* @__PURE__ */ new Map());
|
|
3005
3181
|
const pan = useRef(void 0);
|
|
3006
3182
|
const pinch = useRef(void 0);
|
|
3183
|
+
const frame = useRef(null);
|
|
3184
|
+
const zoomAnchor = useRef(void 0);
|
|
3007
3185
|
const [zoom, setZoom] = useState(1);
|
|
3186
|
+
const zoomTo = (requested, at) => {
|
|
3187
|
+
const next = Math.min(8, Math.max(.25, requested));
|
|
3188
|
+
if (next === zoom) return;
|
|
3189
|
+
const view = host.current?.getBoundingClientRect();
|
|
3190
|
+
const bounds = frame.current?.getBoundingClientRect();
|
|
3191
|
+
if (view && bounds && bounds.width > 0 && bounds.height > 0) {
|
|
3192
|
+
const x = at?.x ?? view.left + view.width / 2;
|
|
3193
|
+
const y = at?.y ?? view.top + view.height / 2;
|
|
3194
|
+
zoomAnchor.current = {
|
|
3195
|
+
x,
|
|
3196
|
+
y,
|
|
3197
|
+
fx: (x - bounds.left) / bounds.width,
|
|
3198
|
+
fy: (y - bounds.top) / bounds.height
|
|
3199
|
+
};
|
|
3200
|
+
}
|
|
3201
|
+
setZoom(next);
|
|
3202
|
+
};
|
|
3203
|
+
useLayoutEffect(() => {
|
|
3204
|
+
const anchor = zoomAnchor.current;
|
|
3205
|
+
zoomAnchor.current = void 0;
|
|
3206
|
+
const container = host.current;
|
|
3207
|
+
const bounds = frame.current?.getBoundingClientRect();
|
|
3208
|
+
if (!anchor || !container || !bounds) return;
|
|
3209
|
+
container.scrollLeft += bounds.left + anchor.fx * bounds.width - anchor.x;
|
|
3210
|
+
container.scrollTop += bounds.top + anchor.fy * bounds.height - anchor.y;
|
|
3211
|
+
}, [zoom]);
|
|
3008
3212
|
const [stroke, setStroke] = useState();
|
|
3009
3213
|
const [pdfPlane, setPdfPlane] = useState();
|
|
3010
3214
|
const [playback, setPlayback] = useState({
|
|
@@ -3023,6 +3227,7 @@ function AuthorMediaView({ sessionId, document: source, activeTool, displayedReg
|
|
|
3023
3227
|
pan.current = void 0;
|
|
3024
3228
|
setStroke(void 0);
|
|
3025
3229
|
setSelection(void 0);
|
|
3230
|
+
zoomAnchor.current = void 0;
|
|
3026
3231
|
setZoom(1);
|
|
3027
3232
|
}, [
|
|
3028
3233
|
source.path,
|
|
@@ -3084,7 +3289,7 @@ function AuthorMediaView({ sessionId, document: source, activeTool, displayedReg
|
|
|
3084
3289
|
const controller = new AbortController();
|
|
3085
3290
|
let dispose;
|
|
3086
3291
|
setError(void 0);
|
|
3087
|
-
loadAuthorMedia(input, controller.signal).then((asset) => {
|
|
3292
|
+
loadAuthorMedia(input, controller.signal, source.imageData).then((asset) => {
|
|
3088
3293
|
if (controller.signal.aborted) {
|
|
3089
3294
|
asset.dispose();
|
|
3090
3295
|
return;
|
|
@@ -3101,7 +3306,7 @@ function AuthorMediaView({ sessionId, document: source, activeTool, displayedReg
|
|
|
3101
3306
|
controller.abort();
|
|
3102
3307
|
dispose?.();
|
|
3103
3308
|
};
|
|
3104
|
-
}, [source.mediaUrl]);
|
|
3309
|
+
}, [source.mediaUrl, source.imageData]);
|
|
3105
3310
|
const seek = (seconds) => {
|
|
3106
3311
|
if (authorDocumentAnnotations(sessionId, source.path).candidate || !Number.isFinite(seconds)) return;
|
|
3107
3312
|
const player = video.current;
|
|
@@ -3277,14 +3482,14 @@ function AuthorMediaView({ sessionId, document: source, activeTool, displayedReg
|
|
|
3277
3482
|
strokeWidth: "5",
|
|
3278
3483
|
strokeLinecap: "round",
|
|
3279
3484
|
strokeLinejoin: "round",
|
|
3280
|
-
className:
|
|
3485
|
+
className: "text-po-magenta"
|
|
3281
3486
|
})
|
|
3282
3487
|
});
|
|
3283
3488
|
const selected = selection?.path === source.path ? selection.rect : void 0;
|
|
3284
3489
|
const selectedPoint = selection?.path === source.path ? selection.point : void 0;
|
|
3285
3490
|
const selectionOverlay = selected === void 0 ? null : /* @__PURE__ */ jsx("div", {
|
|
3286
3491
|
"data-testid": "author-media-selection",
|
|
3287
|
-
className: "pointer-events-none absolute border-2 border-
|
|
3492
|
+
className: "pointer-events-none absolute border-2 border-dashed border-po-magenta bg-po-magenta/10",
|
|
3288
3493
|
style: {
|
|
3289
3494
|
left: `${selected.x * 100}%`,
|
|
3290
3495
|
top: `${selected.y * 100}%`,
|
|
@@ -3295,7 +3500,7 @@ function AuthorMediaView({ sessionId, document: source, activeTool, displayedReg
|
|
|
3295
3500
|
const pointPin = (ordinal, point, key) => /* @__PURE__ */ jsx("span", {
|
|
3296
3501
|
"data-author-point": ordinal,
|
|
3297
3502
|
"data-testid": ordinal === void 0 ? "author-media-point-selection" : void 0,
|
|
3298
|
-
className: `pointer-events-none absolute z-10 flex h-
|
|
3503
|
+
className: `pointer-events-none absolute z-10 flex h-6 min-w-6 -translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full border-2 px-1 text-sm font-semibold ${ordinal === void 0 ? "border-po-magenta bg-po-panel text-po-magenta" : "border-po-panel bg-po-magenta text-po-rail"}`,
|
|
3299
3504
|
style: {
|
|
3300
3505
|
left: `${String(point.x * 100)}%`,
|
|
3301
3506
|
top: `${String(point.y * 100)}%`
|
|
@@ -3304,8 +3509,7 @@ function AuthorMediaView({ sessionId, document: source, activeTool, displayedReg
|
|
|
3304
3509
|
}, key);
|
|
3305
3510
|
const pointSelectionOverlay = selectedPoint === void 0 ? null : pointPin(void 0, selectedPoint, "selection");
|
|
3306
3511
|
return /* @__PURE__ */ jsxs("div", {
|
|
3307
|
-
|
|
3308
|
-
className: "min-h-0 flex-1 overflow-auto p-4",
|
|
3512
|
+
className: "flex min-h-0 flex-1 flex-col",
|
|
3309
3513
|
style: { cursor: activeTool === "draw" || activeTool === "mark" ? "crosshair" : void 0 },
|
|
3310
3514
|
onPointerMove: (event) => {
|
|
3311
3515
|
if (pointers.current.has(event.pointerId)) pointers.current.set(event.pointerId, {
|
|
@@ -3315,7 +3519,10 @@ function AuthorMediaView({ sessionId, document: source, activeTool, displayedReg
|
|
|
3315
3519
|
if (pinch.current !== void 0 && pointers.current.size >= 2) {
|
|
3316
3520
|
const [a, b] = [...pointers.current.values()];
|
|
3317
3521
|
const distance = Math.hypot(a.x - b.x, a.y - b.y);
|
|
3318
|
-
if (pinch.current.distance > 0)
|
|
3522
|
+
if (pinch.current.distance > 0) zoomTo(pinch.current.zoom * distance / pinch.current.distance, {
|
|
3523
|
+
x: (a.x + b.x) / 2,
|
|
3524
|
+
y: (a.y + b.y) / 2
|
|
3525
|
+
});
|
|
3319
3526
|
return;
|
|
3320
3527
|
}
|
|
3321
3528
|
if (pan.current !== void 0 && activeTool === "pan") {
|
|
@@ -3425,141 +3632,44 @@ function AuthorMediaView({ sessionId, document: source, activeTool, displayedReg
|
|
|
3425
3632
|
pan.current = void 0;
|
|
3426
3633
|
setStroke(void 0);
|
|
3427
3634
|
},
|
|
3428
|
-
children: [
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3635
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
3636
|
+
ref: host,
|
|
3637
|
+
className: "@container flex min-h-0 flex-1 flex-col overflow-auto bg-po-deep/50 p-6",
|
|
3638
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
3639
|
+
className: "m-auto flex max-w-full flex-col items-center-safe gap-3",
|
|
3433
3640
|
children: [
|
|
3434
|
-
/* @__PURE__ */ jsx(
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
className: "text-sm text-doom-dim",
|
|
3444
|
-
children: [Math.round(zoom * 100), "%"]
|
|
3445
|
-
}),
|
|
3446
|
-
/* @__PURE__ */ jsx(Button, {
|
|
3447
|
-
size: "md",
|
|
3448
|
-
variant: "outline",
|
|
3449
|
-
"aria-label": "Zoom in",
|
|
3450
|
-
className: "min-h-11 min-w-11",
|
|
3451
|
-
onClick: () => setZoom((current) => Math.min(8, current * 1.5)),
|
|
3452
|
-
children: "+"
|
|
3453
|
-
}),
|
|
3454
|
-
/* @__PURE__ */ jsx(Button, {
|
|
3455
|
-
size: "md",
|
|
3456
|
-
variant: "outline",
|
|
3457
|
-
"aria-label": "Fit canvas",
|
|
3458
|
-
className: "min-h-11",
|
|
3459
|
-
onClick: () => {
|
|
3460
|
-
setZoom(1);
|
|
3461
|
-
if (host.current) {
|
|
3462
|
-
host.current.scrollLeft = 0;
|
|
3463
|
-
host.current.scrollTop = 0;
|
|
3464
|
-
}
|
|
3641
|
+
mediaUrl === void 0 ? /* @__PURE__ */ jsx("p", {
|
|
3642
|
+
className: "text-po-dim",
|
|
3643
|
+
children: error === void 0 ? "Loading media..." : "Media unavailable."
|
|
3644
|
+
}) : source.kind === "image" ? /* @__PURE__ */ jsxs("div", {
|
|
3645
|
+
ref: frame,
|
|
3646
|
+
className: "relative w-max max-w-[100cqw] shadow-sm ring-1 ring-po-border-soft",
|
|
3647
|
+
style: {
|
|
3648
|
+
zoom,
|
|
3649
|
+
touchAction: activeTool === "select" ? "auto" : "none"
|
|
3465
3650
|
},
|
|
3466
|
-
children:
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
children: [
|
|
3480
|
-
/* @__PURE__ */ jsx("img", {
|
|
3481
|
-
ref: image,
|
|
3482
|
-
src: mediaUrl,
|
|
3483
|
-
alt: source.path,
|
|
3484
|
-
draggable: false,
|
|
3485
|
-
className: "block max-w-full"
|
|
3486
|
-
}),
|
|
3487
|
-
selectionOverlay,
|
|
3488
|
-
pointSelectionOverlay,
|
|
3489
|
-
strokeOverlay(stroke),
|
|
3490
|
-
displayedRegions.flatMap(({ ordinal, region }) => {
|
|
3491
|
-
if (region.stroke !== void 0) return [/* @__PURE__ */ jsxs("div", {
|
|
3492
|
-
className: "pointer-events-none absolute inset-0",
|
|
3493
|
-
children: [strokeOverlay(region.stroke, ordinal), pointPin(ordinal, region.stroke[0], `label:${region.id}`)]
|
|
3494
|
-
}, region.id)];
|
|
3495
|
-
if (region.anchor.kind === "image-point") return [pointPin(ordinal, region.anchor.point, region.id)];
|
|
3496
|
-
if (region.anchor.kind !== "image-rect") return [];
|
|
3497
|
-
const { rect } = region.anchor;
|
|
3498
|
-
return [/* @__PURE__ */ jsx("div", {
|
|
3499
|
-
"data-author-region": ordinal,
|
|
3500
|
-
className: "pointer-events-none absolute border border-doom-yellow bg-doom-yellow/10",
|
|
3501
|
-
style: {
|
|
3502
|
-
left: `${String(rect.x * 100)}%`,
|
|
3503
|
-
top: `${String(rect.y * 100)}%`,
|
|
3504
|
-
width: `${String(rect.width * 100)}%`,
|
|
3505
|
-
height: `${String(rect.height * 100)}%`
|
|
3506
|
-
},
|
|
3507
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
3508
|
-
className: "absolute -left-2 -top-2 flex h-4 min-w-4 items-center justify-center rounded-full bg-doom-yellow px-1 text-2xs font-semibold text-doom-deep",
|
|
3509
|
-
children: ordinal
|
|
3510
|
-
})
|
|
3511
|
-
}, region.id)];
|
|
3512
|
-
})
|
|
3513
|
-
]
|
|
3514
|
-
}) : /* @__PURE__ */ jsxs("div", {
|
|
3515
|
-
className: "relative w-fit max-w-full",
|
|
3516
|
-
style: {
|
|
3517
|
-
zoom,
|
|
3518
|
-
touchAction: activeTool === "select" ? "auto" : "none"
|
|
3519
|
-
},
|
|
3520
|
-
children: [/* @__PURE__ */ jsx(MediaPreview, {
|
|
3521
|
-
src: mediaUrl,
|
|
3522
|
-
path: source.path,
|
|
3523
|
-
kind: source.kind === "pdf" ? "pdf" : source.kind === "video" ? "video" : "download",
|
|
3524
|
-
controllerRef: video,
|
|
3525
|
-
pdfControllerRef: pdf,
|
|
3526
|
-
"data-testid": "author-media",
|
|
3527
|
-
videoControls: false,
|
|
3528
|
-
onPlaybackStateChange: (state) => {
|
|
3529
|
-
setPlayback(state);
|
|
3530
|
-
setFrameReady(video.current?.isFrameReady() ?? false);
|
|
3531
|
-
if (state.playing) setSelection(void 0);
|
|
3532
|
-
}
|
|
3533
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
3534
|
-
className: "pointer-events-none absolute",
|
|
3535
|
-
style: source.kind === "pdf" ? pdfPlane === void 0 ? { display: "none" } : {
|
|
3536
|
-
left: pdfPlane.left,
|
|
3537
|
-
top: pdfPlane.top,
|
|
3538
|
-
width: pdfPlane.width,
|
|
3539
|
-
height: pdfPlane.height
|
|
3540
|
-
} : { inset: 0 },
|
|
3541
|
-
children: [
|
|
3542
|
-
selectionOverlay,
|
|
3543
|
-
pointSelectionOverlay,
|
|
3544
|
-
strokeOverlay(stroke),
|
|
3545
|
-
displayedRegions.map(({ ordinal, region }) => {
|
|
3546
|
-
if (region.stroke !== void 0) {
|
|
3547
|
-
const anchor = region.anchor;
|
|
3548
|
-
if (source.kind === "pdf" && (anchor.kind !== "pdf-page-rect" || anchor.page !== pdf.current?.getState().page)) return null;
|
|
3549
|
-
if (source.kind === "video" && (anchor.kind !== "video-time-rect" || playback.playing || Math.abs(anchor.timeSeconds - playback.currentTime) > .1)) return null;
|
|
3550
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
3651
|
+
children: [
|
|
3652
|
+
/* @__PURE__ */ jsx("img", {
|
|
3653
|
+
ref: image,
|
|
3654
|
+
src: mediaUrl,
|
|
3655
|
+
alt: source.path,
|
|
3656
|
+
draggable: false,
|
|
3657
|
+
className: "block max-w-full"
|
|
3658
|
+
}),
|
|
3659
|
+
selectionOverlay,
|
|
3660
|
+
pointSelectionOverlay,
|
|
3661
|
+
strokeOverlay(stroke),
|
|
3662
|
+
displayedRegions.flatMap(({ ordinal, region }) => {
|
|
3663
|
+
if (region.stroke !== void 0) return [/* @__PURE__ */ jsxs("div", {
|
|
3551
3664
|
className: "pointer-events-none absolute inset-0",
|
|
3552
3665
|
children: [strokeOverlay(region.stroke, ordinal), pointPin(ordinal, region.stroke[0], `label:${region.id}`)]
|
|
3553
|
-
}, region.id);
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
if (region.anchor.kind !== "pdf-page-point" && region.anchor.kind !== "pdf-page-rect") return null;
|
|
3557
|
-
if (region.anchor.page !== pdf.current?.getState().page) return null;
|
|
3558
|
-
if (region.anchor.kind === "pdf-page-point") return pointPin(ordinal, region.anchor.point, region.id);
|
|
3666
|
+
}, region.id)];
|
|
3667
|
+
if (region.anchor.kind === "image-point") return [pointPin(ordinal, region.anchor.point, region.id)];
|
|
3668
|
+
if (region.anchor.kind !== "image-rect") return [];
|
|
3559
3669
|
const { rect } = region.anchor;
|
|
3560
|
-
return /* @__PURE__ */ jsx("div", {
|
|
3670
|
+
return [/* @__PURE__ */ jsx("div", {
|
|
3561
3671
|
"data-author-region": ordinal,
|
|
3562
|
-
className:
|
|
3672
|
+
className: REGION_FRAME,
|
|
3563
3673
|
style: {
|
|
3564
3674
|
left: `${String(rect.x * 100)}%`,
|
|
3565
3675
|
top: `${String(rect.y * 100)}%`,
|
|
@@ -3567,55 +3677,169 @@ function AuthorMediaView({ sessionId, document: source, activeTool, displayedReg
|
|
|
3567
3677
|
height: `${String(rect.height * 100)}%`
|
|
3568
3678
|
},
|
|
3569
3679
|
children: /* @__PURE__ */ jsx("span", {
|
|
3570
|
-
className:
|
|
3680
|
+
className: ORDINAL_BADGE,
|
|
3571
3681
|
children: ordinal
|
|
3572
3682
|
})
|
|
3573
|
-
}, region.id);
|
|
3683
|
+
}, region.id)];
|
|
3684
|
+
})
|
|
3685
|
+
]
|
|
3686
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
3687
|
+
ref: frame,
|
|
3688
|
+
className: "relative w-max max-w-[100cqw] shadow-sm ring-1 ring-po-border-soft",
|
|
3689
|
+
style: {
|
|
3690
|
+
zoom,
|
|
3691
|
+
touchAction: activeTool === "select" ? "auto" : "none"
|
|
3692
|
+
},
|
|
3693
|
+
children: [/* @__PURE__ */ jsx(MediaPreview, {
|
|
3694
|
+
src: mediaUrl,
|
|
3695
|
+
path: source.path,
|
|
3696
|
+
kind: source.kind === "pdf" ? "pdf" : source.kind === "video" ? "video" : "download",
|
|
3697
|
+
controllerRef: video,
|
|
3698
|
+
pdfControllerRef: pdf,
|
|
3699
|
+
"data-testid": "author-media",
|
|
3700
|
+
videoControls: false,
|
|
3701
|
+
onPlaybackStateChange: (state) => {
|
|
3702
|
+
setPlayback(state);
|
|
3703
|
+
setFrameReady(video.current?.isFrameReady() ?? false);
|
|
3704
|
+
if (state.playing) setSelection(void 0);
|
|
3574
3705
|
}
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
}
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3706
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
3707
|
+
className: "pointer-events-none absolute",
|
|
3708
|
+
style: source.kind === "pdf" ? pdfPlane === void 0 ? { display: "none" } : {
|
|
3709
|
+
left: pdfPlane.left,
|
|
3710
|
+
top: pdfPlane.top,
|
|
3711
|
+
width: pdfPlane.width,
|
|
3712
|
+
height: pdfPlane.height
|
|
3713
|
+
} : { inset: 0 },
|
|
3714
|
+
children: [
|
|
3715
|
+
selectionOverlay,
|
|
3716
|
+
pointSelectionOverlay,
|
|
3717
|
+
strokeOverlay(stroke),
|
|
3718
|
+
displayedRegions.map(({ ordinal, region }) => {
|
|
3719
|
+
if (region.stroke !== void 0) {
|
|
3720
|
+
const anchor = region.anchor;
|
|
3721
|
+
if (source.kind === "pdf" && (anchor.kind !== "pdf-page-rect" || anchor.page !== pdf.current?.getState().page)) return null;
|
|
3722
|
+
if (source.kind === "video" && (anchor.kind !== "video-time-rect" || playback.playing || Math.abs(anchor.timeSeconds - playback.currentTime) > .1)) return null;
|
|
3723
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3724
|
+
className: "pointer-events-none absolute inset-0",
|
|
3725
|
+
children: [strokeOverlay(region.stroke, ordinal), pointPin(ordinal, region.stroke[0], `label:${region.id}`)]
|
|
3726
|
+
}, region.id);
|
|
3727
|
+
}
|
|
3728
|
+
if (source.kind === "pdf") {
|
|
3729
|
+
if (region.anchor.kind !== "pdf-page-point" && region.anchor.kind !== "pdf-page-rect") return null;
|
|
3730
|
+
if (region.anchor.page !== pdf.current?.getState().page) return null;
|
|
3731
|
+
if (region.anchor.kind === "pdf-page-point") return pointPin(ordinal, region.anchor.point, region.id);
|
|
3732
|
+
const { rect } = region.anchor;
|
|
3733
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3734
|
+
"data-author-region": ordinal,
|
|
3735
|
+
className: REGION_FRAME,
|
|
3736
|
+
style: {
|
|
3737
|
+
left: `${String(rect.x * 100)}%`,
|
|
3738
|
+
top: `${String(rect.y * 100)}%`,
|
|
3739
|
+
width: `${String(rect.width * 100)}%`,
|
|
3740
|
+
height: `${String(rect.height * 100)}%`
|
|
3741
|
+
},
|
|
3742
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
3743
|
+
className: ORDINAL_BADGE,
|
|
3744
|
+
children: ordinal
|
|
3745
|
+
})
|
|
3746
|
+
}, region.id);
|
|
3747
|
+
}
|
|
3748
|
+
if (source.kind !== "video" || playback.playing) return null;
|
|
3749
|
+
if (region.anchor.kind === "video-time-point" && Math.abs(region.anchor.timeSeconds - playback.currentTime) <= .1) return pointPin(ordinal, region.anchor.point, region.id);
|
|
3750
|
+
if (region.anchor.kind !== "video-time-rect" || Math.abs(region.anchor.timeSeconds - playback.currentTime) > .1) return null;
|
|
3751
|
+
const { rect } = region.anchor;
|
|
3752
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3753
|
+
"data-author-region": ordinal,
|
|
3754
|
+
className: REGION_FRAME,
|
|
3755
|
+
style: {
|
|
3756
|
+
left: `${rect.x * 100}%`,
|
|
3757
|
+
top: `${rect.y * 100}%`,
|
|
3758
|
+
width: `${rect.width * 100}%`,
|
|
3759
|
+
height: `${rect.height * 100}%`
|
|
3760
|
+
},
|
|
3761
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
3762
|
+
className: ORDINAL_BADGE,
|
|
3763
|
+
children: ordinal
|
|
3764
|
+
})
|
|
3765
|
+
}, region.id);
|
|
3591
3766
|
})
|
|
3592
|
-
|
|
3767
|
+
]
|
|
3768
|
+
})]
|
|
3769
|
+
}),
|
|
3770
|
+
source.kind === "video" ? /* @__PURE__ */ jsx(AuthorVideoControls, {
|
|
3771
|
+
playback,
|
|
3772
|
+
ready: frameReady,
|
|
3773
|
+
locked: pendingCandidate,
|
|
3774
|
+
marking: activeTool === "mark",
|
|
3775
|
+
onSeek: seek,
|
|
3776
|
+
onToggle: () => {
|
|
3777
|
+
setAuthorToolMode(sessionId, source.path, "select");
|
|
3778
|
+
setSelection(void 0);
|
|
3779
|
+
if (playback.playing) video.current?.pause();
|
|
3780
|
+
else video.current?.play().catch((reason) => setError(String(reason)));
|
|
3781
|
+
},
|
|
3782
|
+
onAnnotate: () => {
|
|
3783
|
+
video.current?.pause();
|
|
3784
|
+
setAuthorToolMode(sessionId, source.path, "mark");
|
|
3785
|
+
}
|
|
3786
|
+
}) : null,
|
|
3787
|
+
error ? /* @__PURE__ */ jsx("output", {
|
|
3788
|
+
className: "text-base text-po-red",
|
|
3789
|
+
children: error
|
|
3790
|
+
}) : null
|
|
3791
|
+
]
|
|
3792
|
+
})
|
|
3793
|
+
}), /* @__PURE__ */ jsxs("footer", {
|
|
3794
|
+
className: "flex shrink-0 items-center gap-2 border-t border-po-border bg-po-bg px-3 py-1.5",
|
|
3795
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
3796
|
+
role: "group",
|
|
3797
|
+
"aria-label": "Canvas zoom",
|
|
3798
|
+
className: "flex items-center gap-2",
|
|
3799
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
3800
|
+
className: "flex items-center rounded-md border border-po-border",
|
|
3801
|
+
children: [
|
|
3802
|
+
/* @__PURE__ */ jsx(Button, {
|
|
3803
|
+
size: "md",
|
|
3804
|
+
variant: "ghost",
|
|
3805
|
+
"aria-label": "Zoom out",
|
|
3806
|
+
className: "min-h-11 min-w-11 rounded-r-none [@media(pointer:fine)]:min-h-8",
|
|
3807
|
+
onClick: () => zoomTo(zoom / 1.5),
|
|
3808
|
+
children: "−"
|
|
3809
|
+
}),
|
|
3810
|
+
/* @__PURE__ */ jsxs("output", {
|
|
3811
|
+
className: "flex min-w-14 items-center justify-center self-stretch border-x border-po-border-soft text-sm tabular-nums text-po-text",
|
|
3812
|
+
children: [Math.round(zoom * 100), "%"]
|
|
3813
|
+
}),
|
|
3814
|
+
/* @__PURE__ */ jsx(Button, {
|
|
3815
|
+
size: "md",
|
|
3816
|
+
variant: "ghost",
|
|
3817
|
+
"aria-label": "Zoom in",
|
|
3818
|
+
className: "min-h-11 min-w-11 rounded-l-none [@media(pointer:fine)]:min-h-8",
|
|
3819
|
+
onClick: () => zoomTo(zoom * 1.5),
|
|
3820
|
+
children: "+"
|
|
3593
3821
|
})
|
|
3594
3822
|
]
|
|
3823
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
3824
|
+
size: "md",
|
|
3825
|
+
variant: "outline",
|
|
3826
|
+
"aria-label": "Fit canvas",
|
|
3827
|
+
className: "min-h-11 [@media(pointer:fine)]:min-h-8",
|
|
3828
|
+
onClick: () => {
|
|
3829
|
+
zoomAnchor.current = void 0;
|
|
3830
|
+
setZoom(1);
|
|
3831
|
+
if (host.current) {
|
|
3832
|
+
host.current.scrollLeft = 0;
|
|
3833
|
+
host.current.scrollTop = 0;
|
|
3834
|
+
}
|
|
3835
|
+
},
|
|
3836
|
+
children: "Fit"
|
|
3595
3837
|
})]
|
|
3596
|
-
}),
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
marking: activeTool === "mark",
|
|
3602
|
-
onSeek: seek,
|
|
3603
|
-
onToggle: () => {
|
|
3604
|
-
setAuthorToolMode(sessionId, source.path, "select");
|
|
3605
|
-
setSelection(void 0);
|
|
3606
|
-
if (playback.playing) video.current?.pause();
|
|
3607
|
-
else video.current?.play().catch((reason) => setError(String(reason)));
|
|
3608
|
-
},
|
|
3609
|
-
onAnnotate: () => {
|
|
3610
|
-
video.current?.pause();
|
|
3611
|
-
setAuthorToolMode(sessionId, source.path, "mark");
|
|
3612
|
-
}
|
|
3613
|
-
}) : null,
|
|
3614
|
-
error ? /* @__PURE__ */ jsx("output", {
|
|
3615
|
-
className: "text-base text-doom-red",
|
|
3616
|
-
children: error
|
|
3617
|
-
}) : null
|
|
3618
|
-
]
|
|
3838
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
3839
|
+
className: "ml-auto text-sm text-po-dim first-letter:uppercase",
|
|
3840
|
+
children: source.kind
|
|
3841
|
+
})]
|
|
3842
|
+
})]
|
|
3619
3843
|
});
|
|
3620
3844
|
}
|
|
3621
3845
|
//#endregion
|
|
@@ -3664,22 +3888,22 @@ function AuthorStructuredView({ sessionId, document, displayedRegions }) {
|
|
|
3664
3888
|
"data-testid": "author-structured",
|
|
3665
3889
|
className: "min-h-0 flex-1 space-y-3 overflow-auto p-4",
|
|
3666
3890
|
children: [!cells ? /* @__PURE__ */ jsx("p", {
|
|
3667
|
-
className: "text-sm text-
|
|
3891
|
+
className: "text-sm text-po-dim",
|
|
3668
3892
|
children: "Slide text view, not a layout-faithful rendering."
|
|
3669
3893
|
}) : null, [...groups].map(([location, fragments]) => /* @__PURE__ */ jsxs("section", {
|
|
3670
3894
|
className: "space-y-1",
|
|
3671
3895
|
children: [/* @__PURE__ */ jsx("h3", {
|
|
3672
|
-
className: "text-sm text-
|
|
3896
|
+
className: "text-sm text-po-dim",
|
|
3673
3897
|
children: location
|
|
3674
3898
|
}), /* @__PURE__ */ jsx("div", {
|
|
3675
3899
|
className: cells ? "flex gap-1" : "space-y-2",
|
|
3676
3900
|
children: fragments.map((fragment) => /* @__PURE__ */ jsxs("div", {
|
|
3677
3901
|
"data-author-fragment": fragment.id,
|
|
3678
3902
|
"data-author-region": displayedByFragment.get(fragment.id),
|
|
3679
|
-
className: `${cells ? "w-40 shrink-0" : ""} relative rounded ${displayedByFragment.has(fragment.id) ? "ring-1 ring-
|
|
3903
|
+
className: `${cells ? "w-40 shrink-0" : ""} relative rounded ${displayedByFragment.has(fragment.id) ? "ring-1 ring-po-yellow" : ""}`,
|
|
3680
3904
|
children: [
|
|
3681
3905
|
displayedByFragment.has(fragment.id) ? /* @__PURE__ */ jsx("span", {
|
|
3682
|
-
className: "absolute -left-1 -top-1 z-10 flex h-4 min-w-4 items-center justify-center rounded-full bg-
|
|
3906
|
+
className: "absolute -left-1 -top-1 z-10 flex h-4 min-w-4 items-center justify-center rounded-full bg-po-yellow px-1 text-2xs font-semibold text-po-deep",
|
|
3683
3907
|
children: displayedByFragment.get(fragment.id)
|
|
3684
3908
|
}) : null,
|
|
3685
3909
|
/* @__PURE__ */ jsxs(Button, {
|
|
@@ -3717,7 +3941,7 @@ function AuthorStructuredView({ sessionId, document, displayedRegions }) {
|
|
|
3717
3941
|
"aria-label": fragment.location,
|
|
3718
3942
|
value: fragment.text,
|
|
3719
3943
|
readOnly: fragment.readOnly === true,
|
|
3720
|
-
className: "w-full rounded border border-
|
|
3944
|
+
className: "w-full rounded border border-po-border bg-po-deep p-2 text-base text-po-text",
|
|
3721
3945
|
onChange: (event) => reviseAuthorFragment(sessionId, document.path, fragment.id, event.target.value)
|
|
3722
3946
|
})
|
|
3723
3947
|
]
|
|
@@ -3759,13 +3983,17 @@ function AuthorTextView({ sessionId, document, preview, displayedRegions }) {
|
|
|
3759
3983
|
label: String(ordinal)
|
|
3760
3984
|
}] : []));
|
|
3761
3985
|
}, [displayedRegions]);
|
|
3762
|
-
|
|
3986
|
+
const lines = (document.content ?? "").split("\n").length;
|
|
3987
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3763
3988
|
ref: host,
|
|
3764
3989
|
className: "flex min-h-0 flex-1 flex-col",
|
|
3765
|
-
children: preview ? /* @__PURE__ */ jsx("div", {
|
|
3990
|
+
children: [preview ? /* @__PURE__ */ jsx("div", {
|
|
3766
3991
|
"data-testid": "author-markdown",
|
|
3767
|
-
className: "min-h-0 flex-1 overflow-auto
|
|
3768
|
-
children: /* @__PURE__ */ jsx(
|
|
3992
|
+
className: "min-h-0 flex-1 overflow-auto bg-po-deep/50 p-6",
|
|
3993
|
+
children: /* @__PURE__ */ jsx("article", {
|
|
3994
|
+
className: "mx-auto min-h-full max-w-3xl rounded-md bg-po-panel px-6 py-8 sm:px-10 text-lg leading-relaxed text-po-text shadow-sm ring-1 ring-po-border-soft",
|
|
3995
|
+
children: /* @__PURE__ */ jsx(Markdown, { text: document.content ?? "" })
|
|
3996
|
+
})
|
|
3769
3997
|
}) : /* @__PURE__ */ jsx(CodeEditor, {
|
|
3770
3998
|
value: document.content ?? "",
|
|
3771
3999
|
path: document.path,
|
|
@@ -3796,17 +4024,41 @@ function AuthorTextView({ sessionId, document, preview, displayedRegions }) {
|
|
|
3796
4024
|
createdAt: Date.now()
|
|
3797
4025
|
});
|
|
3798
4026
|
}
|
|
3799
|
-
})
|
|
4027
|
+
}), /* @__PURE__ */ jsxs("footer", {
|
|
4028
|
+
className: "flex shrink-0 items-center gap-2 border-t border-po-border bg-po-bg px-4 py-1.5 text-sm text-po-dim",
|
|
4029
|
+
children: [
|
|
4030
|
+
/* @__PURE__ */ jsx("span", {
|
|
4031
|
+
className: "first-letter:uppercase",
|
|
4032
|
+
children: document.kind
|
|
4033
|
+
}),
|
|
4034
|
+
/* @__PURE__ */ jsx("span", {
|
|
4035
|
+
"aria-hidden": "true",
|
|
4036
|
+
children: "·"
|
|
4037
|
+
}),
|
|
4038
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
4039
|
+
lines,
|
|
4040
|
+
" line",
|
|
4041
|
+
lines === 1 ? "" : "s"
|
|
4042
|
+
] })
|
|
4043
|
+
]
|
|
4044
|
+
})]
|
|
3800
4045
|
});
|
|
3801
4046
|
}
|
|
3802
4047
|
//#endregion
|
|
3803
4048
|
//#region src/extensions/workspaces/sessions/(frontend)/_components/AuthorDocumentPanel.tsx
|
|
4049
|
+
const MOBILE_TOOL_GLYPHS = {
|
|
4050
|
+
select: "↖",
|
|
4051
|
+
pan: "✋",
|
|
4052
|
+
mark: "⌗",
|
|
4053
|
+
comment: "●",
|
|
4054
|
+
draw: "✎"
|
|
4055
|
+
};
|
|
3804
4056
|
const pendingCanvases = /* @__PURE__ */ new Map();
|
|
3805
4057
|
const canvasOwners = /* @__PURE__ */ new Map();
|
|
3806
4058
|
function tabId(path) {
|
|
3807
4059
|
let hash = 2166136261;
|
|
3808
4060
|
for (const char of normalizeAuthorPath(path)) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619);
|
|
3809
|
-
return
|
|
4061
|
+
return `${AUTHOR_FILE_TAB_PREFIX}${(hash >>> 0).toString(36)}`;
|
|
3810
4062
|
}
|
|
3811
4063
|
/** Unsent annotations only. Sent ones live in the request log, not on the canvas. */
|
|
3812
4064
|
function displayedAuthorRegions(workspace) {
|
|
@@ -3816,19 +4068,8 @@ function displayedAuthorRegions(workspace) {
|
|
|
3816
4068
|
}));
|
|
3817
4069
|
}
|
|
3818
4070
|
function AuthorDocumentPanel(props) {
|
|
3819
|
-
if (!props.activeMinorModes?.includes("author")) {
|
|
3820
|
-
const fallback = props.fileTabFor(props.path);
|
|
3821
|
-
if (fallback !== void 0 && fallback.id !== tabId(props.path)) return /* @__PURE__ */ jsx(fallback.panel, { ...props });
|
|
3822
|
-
return /* @__PURE__ */ jsx("p", {
|
|
3823
|
-
"data-testid": "author-mode-inactive",
|
|
3824
|
-
className: "p-3 text-sm text-doom-dim",
|
|
3825
|
-
children: "No file viewer is available for this path."
|
|
3826
|
-
});
|
|
3827
|
-
}
|
|
3828
|
-
return /* @__PURE__ */ jsx(ActiveAuthorDocumentPanel, { ...props });
|
|
3829
|
-
}
|
|
3830
|
-
function ActiveAuthorDocumentPanel(props) {
|
|
3831
4071
|
const { sessionId, statuses } = props;
|
|
4072
|
+
const agentEnabled = props.activeMinorModes?.includes(AUTHOR_MODE_ID) === true;
|
|
3832
4073
|
const path = useStore(canvasPaths, (state) => sessionId === null ? props.path : state[authorDocumentKey(sessionId, props.path)] ?? props.path);
|
|
3833
4074
|
const alias = useStore(canvasAliases, (state) => props.alias ?? (sessionId === null ? void 0 : state[authorDocumentKey(sessionId, props.path)]));
|
|
3834
4075
|
const loadFailure = useStore(canvasFailures, (state) => sessionId === null ? void 0 : state[authorDocumentKey(sessionId, props.path)]);
|
|
@@ -3859,7 +4100,7 @@ function ActiveAuthorDocumentPanel(props) {
|
|
|
3859
4100
|
return () => releaseAuthorDocumentFocus(sessionId, generation);
|
|
3860
4101
|
}, [path, sessionId]);
|
|
3861
4102
|
useEffect(() => {
|
|
3862
|
-
if (kind === void 0 || sessionId === null || alias === void 0) return;
|
|
4103
|
+
if (!agentEnabled || kind === void 0 || sessionId === null || alias === void 0) return;
|
|
3863
4104
|
const profiles = authorProfilesForDocument(sessionId, path, kind);
|
|
3864
4105
|
let release;
|
|
3865
4106
|
let cancelled = false;
|
|
@@ -3872,13 +4113,14 @@ function ActiveAuthorDocumentPanel(props) {
|
|
|
3872
4113
|
release?.();
|
|
3873
4114
|
};
|
|
3874
4115
|
}, [
|
|
4116
|
+
agentEnabled,
|
|
3875
4117
|
alias,
|
|
3876
4118
|
kind,
|
|
3877
4119
|
path,
|
|
3878
4120
|
sessionId
|
|
3879
4121
|
]);
|
|
3880
4122
|
if (document === void 0 || sessionId === null) return /* @__PURE__ */ jsx("p", {
|
|
3881
|
-
className: "p-4 text-sm text-
|
|
4123
|
+
className: "p-4 text-sm text-po-dim",
|
|
3882
4124
|
children: loadFailure ?? status ?? "Loading document..."
|
|
3883
4125
|
});
|
|
3884
4126
|
const save = async () => {
|
|
@@ -3935,11 +4177,28 @@ function ActiveAuthorDocumentPanel(props) {
|
|
|
3935
4177
|
className: "flex min-h-0 flex-1 flex-col",
|
|
3936
4178
|
children: [
|
|
3937
4179
|
/* @__PURE__ */ jsxs("header", {
|
|
3938
|
-
className: "flex items-center gap-2 border-b border-
|
|
4180
|
+
className: "flex items-center gap-2 border-b border-po-border bg-po-bg px-4 py-2",
|
|
3939
4181
|
children: [
|
|
4182
|
+
/* @__PURE__ */ jsx(FileIcon, {
|
|
4183
|
+
"aria-hidden": "true",
|
|
4184
|
+
className: "size-4 shrink-0 text-po-dim"
|
|
4185
|
+
}),
|
|
3940
4186
|
/* @__PURE__ */ jsxs("strong", {
|
|
3941
|
-
className: "min-w-0
|
|
3942
|
-
children: [alias === void 0 ? "" : `${alias} · `, document.title ??
|
|
4187
|
+
className: "min-w-0 truncate text-base font-medium text-po-hi",
|
|
4188
|
+
children: [alias === void 0 ? "" : `${alias} · `, document.title ?? /* @__PURE__ */ jsx("span", {
|
|
4189
|
+
className: "font-mono",
|
|
4190
|
+
children: document.path
|
|
4191
|
+
})]
|
|
4192
|
+
}),
|
|
4193
|
+
/* @__PURE__ */ jsx("span", {
|
|
4194
|
+
className: "min-w-0 flex-1",
|
|
4195
|
+
children: document.version === document.savedVersion ? null : /* @__PURE__ */ jsxs("span", {
|
|
4196
|
+
className: "inline-flex items-center gap-1.5 text-sm text-po-dim",
|
|
4197
|
+
children: [/* @__PURE__ */ jsx(Dot, {
|
|
4198
|
+
tone: "green",
|
|
4199
|
+
size: "md"
|
|
4200
|
+
}), "unsaved"]
|
|
4201
|
+
})
|
|
3943
4202
|
}),
|
|
3944
4203
|
PreviewPanel !== void 0 && previewSource !== void 0 ? /* @__PURE__ */ jsx(Button, {
|
|
3945
4204
|
size: "xs",
|
|
@@ -3951,17 +4210,21 @@ function ActiveAuthorDocumentPanel(props) {
|
|
|
3951
4210
|
children: previewOpen ? "Source" : "Preview"
|
|
3952
4211
|
}) : null,
|
|
3953
4212
|
document.kind === "markdown" ? /* @__PURE__ */ jsx(Button, {
|
|
3954
|
-
size: "
|
|
3955
|
-
variant: "
|
|
4213
|
+
size: "sm",
|
|
4214
|
+
variant: "outline",
|
|
4215
|
+
className: "min-h-11 [@media(pointer:fine)]:min-h-8",
|
|
3956
4216
|
"data-testid": "author-markdown-toggle",
|
|
3957
4217
|
onClick: () => setMarkdownPreview(!markdownPreview),
|
|
3958
|
-
children:
|
|
4218
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
4219
|
+
className: "first-letter:uppercase",
|
|
4220
|
+
children: markdownPreview ? "edit" : "preview"
|
|
4221
|
+
})
|
|
3959
4222
|
}) : null,
|
|
3960
4223
|
document.kind === "video" ? /* @__PURE__ */ jsx("span", {
|
|
3961
|
-
className: "text-base text-
|
|
4224
|
+
className: "text-base text-po-dim",
|
|
3962
4225
|
children: "Video feedback"
|
|
3963
4226
|
}) : /* @__PURE__ */ jsx(Button, {
|
|
3964
|
-
className: "min-h-11 px-4 text-base",
|
|
4227
|
+
className: "min-h-11 px-4 text-base [@media(pointer:fine)]:min-h-8",
|
|
3965
4228
|
variant: "outline",
|
|
3966
4229
|
"data-testid": "author-save",
|
|
3967
4230
|
disabled: document.revisions.length === 0 || document.savingVersion !== void 0,
|
|
@@ -3971,7 +4234,7 @@ function ActiveAuthorDocumentPanel(props) {
|
|
|
3971
4234
|
]
|
|
3972
4235
|
}),
|
|
3973
4236
|
status === void 0 ? null : /* @__PURE__ */ jsx("output", {
|
|
3974
|
-
className: "px-4 py-1 text-sm text-
|
|
4237
|
+
className: "px-4 py-1 text-sm text-po-dim",
|
|
3975
4238
|
children: status
|
|
3976
4239
|
}),
|
|
3977
4240
|
[
|
|
@@ -3981,21 +4244,28 @@ function ActiveAuthorDocumentPanel(props) {
|
|
|
3981
4244
|
"story-preview"
|
|
3982
4245
|
].includes(document.kind) ? /* @__PURE__ */ jsx("nav", {
|
|
3983
4246
|
"aria-label": "Author canvas tools",
|
|
3984
|
-
className: "flex shrink-0 gap-1 overflow-x-auto border-b border-
|
|
4247
|
+
className: "flex shrink-0 gap-1.5 overflow-x-auto border-b border-po-border bg-po-bg px-3 py-2 sm:hidden",
|
|
3985
4248
|
children: [
|
|
3986
4249
|
"select",
|
|
3987
4250
|
"pan",
|
|
3988
4251
|
"mark",
|
|
3989
4252
|
"comment",
|
|
3990
4253
|
"draw"
|
|
3991
|
-
].map((mode) => /* @__PURE__ */
|
|
3992
|
-
size: "
|
|
3993
|
-
variant:
|
|
3994
|
-
className:
|
|
4254
|
+
].map((mode) => /* @__PURE__ */ jsxs(Button, {
|
|
4255
|
+
size: "md",
|
|
4256
|
+
variant: "outline",
|
|
4257
|
+
className: `min-w-16 shrink-0 ${authorToolClassName(activeTool === mode, mode !== "select" && mode !== "pan")}`,
|
|
3995
4258
|
"aria-label": `${mode} tool`,
|
|
3996
4259
|
"aria-pressed": activeTool === mode,
|
|
3997
4260
|
onClick: () => setAuthorToolMode(sessionId, document.path, mode),
|
|
3998
|
-
children:
|
|
4261
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
4262
|
+
"aria-hidden": "true",
|
|
4263
|
+
className: "text-lg leading-none",
|
|
4264
|
+
children: MOBILE_TOOL_GLYPHS[mode]
|
|
4265
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
4266
|
+
className: "text-sm first-letter:uppercase",
|
|
4267
|
+
children: mode
|
|
4268
|
+
})]
|
|
3999
4269
|
}, mode))
|
|
4000
4270
|
}) : null,
|
|
4001
4271
|
/* @__PURE__ */ jsxs("div", {
|
|
@@ -4041,7 +4311,7 @@ function ActiveAuthorDocumentPanel(props) {
|
|
|
4041
4311
|
"video",
|
|
4042
4312
|
"story-preview"
|
|
4043
4313
|
].includes(document.kind) ? /* @__PURE__ */ jsx("div", {
|
|
4044
|
-
className: "max-h-56 shrink-0 overflow-y-auto border-t border-
|
|
4314
|
+
className: "max-h-56 shrink-0 overflow-y-auto border-t border-po-border bg-po-bg p-3 sm:hidden",
|
|
4045
4315
|
children: /* @__PURE__ */ jsx(AuthorFeedbackControls, {
|
|
4046
4316
|
sessionId,
|
|
4047
4317
|
document,
|
|
@@ -4070,13 +4340,13 @@ function authorFileTab(path, preferredAlias) {
|
|
|
4070
4340
|
const normalized = normalizeAuthorPath(path);
|
|
4071
4341
|
return {
|
|
4072
4342
|
id: tabId(normalized),
|
|
4073
|
-
label: preferredAlias === void 0 ? normalized
|
|
4343
|
+
label: preferredAlias === void 0 ? authorFileName(normalized) : `${preferredAlias} · ${authorFileName(normalized)}`,
|
|
4074
4344
|
retainComposer: true,
|
|
4075
4345
|
composerContext(sessionId) {
|
|
4076
4346
|
const key = authorDocumentKey(sessionId, normalized);
|
|
4077
4347
|
const documentPath = canvasPaths.state[key] ?? normalized;
|
|
4078
4348
|
const alias = canvasAliases.state[key];
|
|
4079
|
-
const name = documentPath
|
|
4349
|
+
const name = authorFileName(documentPath);
|
|
4080
4350
|
return {
|
|
4081
4351
|
kind: "author-document",
|
|
4082
4352
|
source: "author",
|
|
@@ -4221,6 +4491,130 @@ var Author_author_open_source_web_default = defineFill({
|
|
|
4221
4491
|
data: authorOpenSourceAction
|
|
4222
4492
|
});
|
|
4223
4493
|
//#endregion
|
|
4494
|
+
//#region src/extensions/workspaces/sessions/(frontend)/_lib/authorImageFeedback.ts
|
|
4495
|
+
const MAX_IMAGE_BYTES = 20971520;
|
|
4496
|
+
const PNG_SIGNATURE = [
|
|
4497
|
+
137,
|
|
4498
|
+
80,
|
|
4499
|
+
78,
|
|
4500
|
+
71,
|
|
4501
|
+
13,
|
|
4502
|
+
10,
|
|
4503
|
+
26,
|
|
4504
|
+
10
|
|
4505
|
+
];
|
|
4506
|
+
/** Recheck caller identity and bytes before adapting an immutable image to author tools. */
|
|
4507
|
+
async function authorImageFeedbackDocument(target, sessionId, signal) {
|
|
4508
|
+
if (target.sessionId !== sessionId || target.mimeType !== "image/png" || !/^[a-zA-Z0-9_-]+$/u.test(sessionId) || target.id.length === 0 || target.originalPath.length === 0 || !/^[a-f0-9]{64}$/u.test(target.sourceSha256)) throw new Error("Image feedback requires a verified artifact from the active session.");
|
|
4509
|
+
if (target.data.length > Math.ceil(MAX_IMAGE_BYTES / 3) * 4 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u.test(target.data)) throw new Error("Image feedback PNG exceeds the size limit or is invalid.");
|
|
4510
|
+
const binary = atob(target.data);
|
|
4511
|
+
const data = Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
4512
|
+
if (data.length > MAX_IMAGE_BYTES || PNG_SIGNATURE.some((byte, index) => data[index] !== byte)) throw new Error("Image feedback requires PNG bytes.");
|
|
4513
|
+
signal.throwIfAborted();
|
|
4514
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", data));
|
|
4515
|
+
const sha256 = Array.from(digest, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
4516
|
+
signal.throwIfAborted();
|
|
4517
|
+
if (sha256 !== target.sourceSha256) throw new Error("Image feedback source digest does not match.");
|
|
4518
|
+
return {
|
|
4519
|
+
path: `image-artifact/${encodeURIComponent(target.id)}/${sha256}.png`,
|
|
4520
|
+
kind: "image",
|
|
4521
|
+
title: "Generated image feedback",
|
|
4522
|
+
sourceSha256: sha256,
|
|
4523
|
+
imageData: target.data,
|
|
4524
|
+
mediaUrl: `image-artifact:${sha256}`,
|
|
4525
|
+
imageArtifact: {
|
|
4526
|
+
id: target.id,
|
|
4527
|
+
sessionId,
|
|
4528
|
+
originalPath: target.originalPath,
|
|
4529
|
+
sourceSha256: sha256
|
|
4530
|
+
}
|
|
4531
|
+
};
|
|
4532
|
+
}
|
|
4533
|
+
//#endregion
|
|
4534
|
+
//#region src/extensions/workspaces/sessions/(frontend)/_components/AuthorImageFeedbackPanel.tsx
|
|
4535
|
+
/** Optional author controls for immutable session image artifacts. */
|
|
4536
|
+
function AuthorImageFeedbackPanel({ target, sessionId, submitCapture }) {
|
|
4537
|
+
const enabled = sessionId === target.sessionId;
|
|
4538
|
+
const [document, setDocument] = useState();
|
|
4539
|
+
const [error, setError] = useState();
|
|
4540
|
+
useEffect(() => {
|
|
4541
|
+
setDocument(void 0);
|
|
4542
|
+
setError(void 0);
|
|
4543
|
+
if (!enabled || sessionId === null) return;
|
|
4544
|
+
const controller = new AbortController();
|
|
4545
|
+
let generation;
|
|
4546
|
+
authorImageFeedbackDocument(target, sessionId, controller.signal).then((input) => {
|
|
4547
|
+
if (controller.signal.aborted) return;
|
|
4548
|
+
setDocument(putAuthorDocument(sessionId, input));
|
|
4549
|
+
generation = focusAuthorDocument(sessionId, input.path);
|
|
4550
|
+
}).catch((reason) => {
|
|
4551
|
+
if (!controller.signal.aborted) setError(reason instanceof Error ? reason.message : String(reason));
|
|
4552
|
+
});
|
|
4553
|
+
return () => {
|
|
4554
|
+
controller.abort();
|
|
4555
|
+
if (generation !== void 0) releaseAuthorDocumentFocus(sessionId, generation);
|
|
4556
|
+
};
|
|
4557
|
+
}, [
|
|
4558
|
+
enabled,
|
|
4559
|
+
sessionId,
|
|
4560
|
+
target
|
|
4561
|
+
]);
|
|
4562
|
+
const drafts = useStore(authorWorkspace.store, (state) => authorDocumentAnnotations(sessionId, document?.path ?? "", state));
|
|
4563
|
+
const tool = useStore(authorWorkspace.store, (state) => sessionId === null ? "select" : state.sessions[sessionId]?.toolsByDocument[document?.path ?? ""] ?? "select");
|
|
4564
|
+
if (!enabled || sessionId === null) return null;
|
|
4565
|
+
if (error) return /* @__PURE__ */ jsx("output", { children: error });
|
|
4566
|
+
if (!document || document.imageArtifact?.id !== target.id || document.sourceSha256 !== target.sourceSha256) return /* @__PURE__ */ jsx("output", { children: "Loading image feedback…" });
|
|
4567
|
+
const displayedRegions = drafts.annotations.map((region, index) => ({
|
|
4568
|
+
ordinal: index + 1,
|
|
4569
|
+
region
|
|
4570
|
+
}));
|
|
4571
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
4572
|
+
className: "flex min-h-0 flex-col gap-3",
|
|
4573
|
+
"data-testid": "author-image-feedback",
|
|
4574
|
+
children: [
|
|
4575
|
+
/* @__PURE__ */ jsx("p", {
|
|
4576
|
+
className: "text-sm text-po-dim",
|
|
4577
|
+
children: "Original preserved. Submit annotations to request a new refinement."
|
|
4578
|
+
}),
|
|
4579
|
+
/* @__PURE__ */ jsx("div", {
|
|
4580
|
+
className: "sm:hidden",
|
|
4581
|
+
children: /* @__PURE__ */ jsx(AuthorToolPalette, {
|
|
4582
|
+
sessionId,
|
|
4583
|
+
path: document.path,
|
|
4584
|
+
kind: "image",
|
|
4585
|
+
activeTool: tool
|
|
4586
|
+
})
|
|
4587
|
+
}),
|
|
4588
|
+
/* @__PURE__ */ jsx(AuthorMediaView, {
|
|
4589
|
+
sessionId,
|
|
4590
|
+
document,
|
|
4591
|
+
activeTool: tool,
|
|
4592
|
+
displayedRegions,
|
|
4593
|
+
pendingCandidate: drafts.candidate !== void 0
|
|
4594
|
+
}),
|
|
4595
|
+
/* @__PURE__ */ jsx("div", {
|
|
4596
|
+
className: "sm:hidden",
|
|
4597
|
+
children: /* @__PURE__ */ jsx(AuthorFeedbackControls, {
|
|
4598
|
+
sessionId,
|
|
4599
|
+
document,
|
|
4600
|
+
drafts,
|
|
4601
|
+
submitCapture
|
|
4602
|
+
})
|
|
4603
|
+
})
|
|
4604
|
+
]
|
|
4605
|
+
});
|
|
4606
|
+
}
|
|
4607
|
+
//#endregion
|
|
4608
|
+
//#region src/extensions/workspaces/sessions/(frontend)/fill/ImageFeedback.author.image-feedback.web.ts
|
|
4609
|
+
var ImageFeedback_author_image_feedback_web_default = defineFill({
|
|
4610
|
+
slot: "author.image-feedback",
|
|
4611
|
+
id: "image-feedback",
|
|
4612
|
+
data: {
|
|
4613
|
+
version: 1,
|
|
4614
|
+
panel: AuthorImageFeedbackPanel
|
|
4615
|
+
}
|
|
4616
|
+
});
|
|
4617
|
+
//#endregion
|
|
4224
4618
|
//#region src/extensions/workspaces/sessions/(frontend)/leader/author-toggle.web.ts
|
|
4225
4619
|
var author_toggle_web_default = defineLeaderBinding({
|
|
4226
4620
|
id: "author.toggle",
|
|
@@ -4443,12 +4837,13 @@ function recordAuthorCaptureStatus(event) {
|
|
|
4443
4837
|
if (request === void 0 || [
|
|
4444
4838
|
"COMPLETE",
|
|
4445
4839
|
"FAILED",
|
|
4446
|
-
"CANCELLED"
|
|
4840
|
+
"CANCELLED",
|
|
4841
|
+
"UNTRACKED"
|
|
4447
4842
|
].includes(request.status)) return;
|
|
4448
4843
|
if (event.status === "queued") return;
|
|
4449
4844
|
updateAuthorRequest(event.sessionId, request.id, (record) => ({
|
|
4450
4845
|
...record,
|
|
4451
|
-
status: event.status === "working" ? "CHANGING" : event.status === "completed" ? "COMPLETE" : "FAILED",
|
|
4846
|
+
status: event.status === "working" ? "CHANGING" : event.status === "completed" ? "COMPLETE" : event.status === "untracked" ? "UNTRACKED" : "FAILED",
|
|
4452
4847
|
currentOperation: event.status === "working" ? "Agent is working on this request" : void 0,
|
|
4453
4848
|
pendingRegions: event.status === "completed" ? [] : record.pendingRegions,
|
|
4454
4849
|
error: event.status === "error" ? event.error || "Request execution failed." : void 0,
|
|
@@ -4480,6 +4875,19 @@ var mode_web_default = defineMinorModeFile({
|
|
|
4480
4875
|
order: 45
|
|
4481
4876
|
});
|
|
4482
4877
|
//#endregion
|
|
4878
|
+
//#region src/types/imageFeedback.ts
|
|
4879
|
+
function parseAuthorImageFeedbackContribution(input) {
|
|
4880
|
+
if (input === null || typeof input !== "object") return null;
|
|
4881
|
+
const value = input;
|
|
4882
|
+
return value.version === 1 && typeof value.panel === "function" ? value : null;
|
|
4883
|
+
}
|
|
4884
|
+
//#endregion
|
|
4885
|
+
//#region src/extensions/workspaces/sessions/(frontend)/slot/image-feedback.web.ts
|
|
4886
|
+
var image_feedback_web_default = defineSlotFile({
|
|
4887
|
+
slot: "author.image-feedback",
|
|
4888
|
+
parse: parseAuthorImageFeedbackContribution
|
|
4889
|
+
});
|
|
4890
|
+
//#endregion
|
|
4483
4891
|
//#region src/extensions/workspaces/sessions/(frontend)/slot/open-source.web.ts
|
|
4484
4892
|
var open_source_web_default = defineSlotFile({
|
|
4485
4893
|
slot: "author.open-source",
|
|
@@ -4539,7 +4947,7 @@ function DescribeAuthorToolsToolCard({ toolName, args, result, running, isError
|
|
|
4539
4947
|
title: toolName,
|
|
4540
4948
|
children: /* @__PURE__ */ jsx("span", {
|
|
4541
4949
|
"data-testid": `tool-call-${toolName}`,
|
|
4542
|
-
className: "min-w-0 flex-1 truncate text-
|
|
4950
|
+
className: "min-w-0 flex-1 truncate text-po-text",
|
|
4543
4951
|
children: describeAuthorToolsCallSummary(args)
|
|
4544
4952
|
})
|
|
4545
4953
|
}), view.shown.length > 0 || running || isError ? /* @__PURE__ */ jsxs(MessageItemBody, {
|
|
@@ -4599,7 +5007,7 @@ function OpenAuthoringFileToolCard({ args, result, running, isError, output, ope
|
|
|
4599
5007
|
title: "open_authoring_file",
|
|
4600
5008
|
children: /* @__PURE__ */ jsx("span", {
|
|
4601
5009
|
"data-testid": "tool-call-open_authoring_file",
|
|
4602
|
-
className: "min-w-0 flex-1 truncate text-
|
|
5010
|
+
className: "min-w-0 flex-1 truncate text-po-text",
|
|
4603
5011
|
children: alias === void 0 ? requestedPath : `${alias} · ${requestedPath}`
|
|
4604
5012
|
})
|
|
4605
5013
|
}), /* @__PURE__ */ jsxs(MessageItemBody, {
|
|
@@ -4672,7 +5080,7 @@ function UseAuthorToolsToolCard({ toolName, args, result, running, isError }) {
|
|
|
4672
5080
|
title: toolName,
|
|
4673
5081
|
children: /* @__PURE__ */ jsx("span", {
|
|
4674
5082
|
"data-testid": `tool-call-${toolName}`,
|
|
4675
|
-
className: "min-w-0 flex-1 truncate text-
|
|
5083
|
+
className: "min-w-0 flex-1 truncate text-po-text",
|
|
4676
5084
|
children: useAuthorToolsCallSummary(args)
|
|
4677
5085
|
})
|
|
4678
5086
|
}), view.shown.length > 0 || running || isError ? /* @__PURE__ */ jsxs(MessageItemBody, {
|
|
@@ -4725,7 +5133,10 @@ const webPlugin = defineWebPlugin({
|
|
|
4725
5133
|
return [via({
|
|
4726
5134
|
slot: "author.open-source",
|
|
4727
5135
|
id: "author"
|
|
4728
|
-
}, Author_author_open_source_web_default)
|
|
5136
|
+
}, Author_author_open_source_web_default), via({
|
|
5137
|
+
slot: "author.image-feedback",
|
|
5138
|
+
id: "image-feedback"
|
|
5139
|
+
}, ImageFeedback_author_image_feedback_web_default)];
|
|
4729
5140
|
},
|
|
4730
5141
|
get leaderBindings() {
|
|
4731
5142
|
return [via({ id: "author.author-toggle" }, author_toggle_web_default)];
|
|
@@ -4735,6 +5146,7 @@ const webPlugin = defineWebPlugin({
|
|
|
4735
5146
|
},
|
|
4736
5147
|
get slots() {
|
|
4737
5148
|
return [
|
|
5149
|
+
via({ slot: "author.image-feedback" }, image_feedback_web_default),
|
|
4738
5150
|
via({ slot: "author.open-source" }, open_source_web_default),
|
|
4739
5151
|
via({ slot: "author.preview-action" }, preview_action_web_default),
|
|
4740
5152
|
via({ slot: "author.preview-provider" }, preview_provider_web_default)
|