@agentrouter-top/relay-dsh-plugin-codex 0.2.2-agentrouter.5 → 0.2.2-agentrouter.7

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.
@@ -12,5 +12,10 @@ export function readActivityPayload(value) {
12
12
  for (const key of ["summary", "input", "output", "exitCode", "commandActions"]) {
13
13
  if (activity[key] !== undefined && typeof activity[key] !== "string") return null;
14
14
  }
15
+ if (activity.imageGeneration !== undefined) {
16
+ const image = activity.imageGeneration;
17
+ if (activity.type !== "imageGeneration" || !image || typeof image !== "object" || image.version !== 1
18
+ || (image.attachmentId !== undefined && (typeof image.attachmentId !== "string" || !image.attachmentId))) return null;
19
+ }
15
20
  return value;
16
21
  }
@@ -0,0 +1,19 @@
1
+ # Image generation presentation v1
2
+
3
+ Relay projects native Codex `imageGeneration` item start/completion into a dot-matrix
4
+ waiting frame. This is not a partial image, estimated progress, or a new image tool.
5
+ Only a successful attachment import associates `activity.imageGeneration.attachmentId`
6
+ with the existing DSH image block. Projection matches that exact ID within the same
7
+ DSH step, keeps the generation key, and consumes only the matched image occurrence.
8
+ Parallel images cannot be paired by arrival order. Old activity without this optional
9
+ version-1 metadata, MCP images and image-view results keep their existing renderers.
10
+
11
+ The enhanced Codex process projection owns the frame. Native/fallback DSH transcripts
12
+ retain their tool and image rows. Failure/interruption stops the animation without
13
+ adding a warning badge; existing actual turn errors remain available. Reduced-motion
14
+ uses a static dot field. The final result uses DSH's public `renderMessageImages`, so
15
+ the independent Viewer plugin continues to handle viewing and downloading.
16
+
17
+ No Codex prompt, gateway, credentials, tool implementation, Desktop code, or session
18
+ history is modified. Image URLs and private filesystem paths are not added to the
19
+ presentation metadata.
@@ -0,0 +1,19 @@
1
+ # Image generation motion
2
+
3
+ Relay owns the approved iridescent dot field and the native-image reveal. This
4
+ is an indeterminate status, not server progress. No model, tool, authentication,
5
+ attachment import, Viewer or Desktop behavior is changed.
6
+
7
+ The pending card is 4:3 and at most 480 px wide. Six interpolated colors and a
8
+ soft moving ribbon replace the grey grid. Light/dark mode comes exclusively
9
+ from the public DSH theme service. No host DOM probing or additional dependency.
10
+ Canvas updates are imperative, at most about 33 paints per second, DPR capped
11
+ at 2. Hidden/offscreen/reduced-motion frames do not animate; completion, failure,
12
+ interruption and unmount dispose subscriptions. Document/media listeners are
13
+ shared across pending images.
14
+
15
+ Generated frames stay mounted when process details collapse. Completion fades
16
+ the field into DSH's native image renderer without duplicate attachments. The
17
+ actual result keeps its native aspect ratio and Viewer actions. History opens
18
+ immediately without replaying the animation. Design-preview palette/demo
19
+ controls and synthetic images are not shipped.
package/lib/client.js CHANGED
@@ -193,13 +193,13 @@ window.__ModuleLoader__.load({
193
193
  }
194
194
  //#endregion
195
195
  //#region \0relay-css-module:.\src\client\AdvancedDebug.module.css.mjs
196
- const css$3 = ".Y4qSZW_section{width:100%;max-width:780px;color:var(--dsw-alias-label-primary)}.Y4qSZW_settingRow{border-bottom:1px solid var(--dsw-alias-border-l2);justify-content:space-between;align-items:center;gap:20px;min-height:58px;padding:8px 0;display:flex}.Y4qSZW_statusRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:flex-start;gap:10px;min-height:70px;padding:10px 0;display:flex}.Y4qSZW_statusDot{background:var(--dsw-alias-label-tertiary);border-radius:50%;flex:none;width:8px;height:8px;margin-top:6px}.Y4qSZW_statusRow[data-codex-status=connected] .Y4qSZW_statusDot{background:#27a65b}.Y4qSZW_statusRow[data-codex-status=connection-failed] .Y4qSZW_statusDot,.Y4qSZW_statusRow[data-codex-status=unavailable] .Y4qSZW_statusDot,.Y4qSZW_statusRow[data-codex-status=rebind-required] .Y4qSZW_statusDot{background:#d94841}.Y4qSZW_settingCopy code{color:var(--dsw-alias-label-tertiary);font-size:11px}.Y4qSZW_statusBadge{border-radius:999px;padding:2px 7px;font-size:11px;font-weight:600;line-height:16px}.Y4qSZW_statusError{color:#d94841;background:#d9484124}.Y4qSZW_statusRebind{color:#a95d00;background:#c26a0029}.Y4qSZW_settingCopy{flex-direction:column;gap:2px;min-width:0;display:flex}.Y4qSZW_settingCopy strong{font-size:14px;font-weight:500;line-height:20px}.Y4qSZW_settingCopy span{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.Y4qSZW_switch{cursor:pointer;flex:none;width:36px;height:20px;display:inline-flex;position:relative}.Y4qSZW_switch input{opacity:0;width:1px;height:1px;position:absolute}.Y4qSZW_switch span{background:var(--dsw-alias-fill-l2);border-radius:10px;width:100%;transition:background-color .12s}.Y4qSZW_switch span:after{background:var(--dsw-alias-bg-layer-1);content:\"\";border-radius:50%;width:16px;height:16px;transition:transform .12s;position:absolute;top:2px;left:2px;box-shadow:0 1px 3px #0003}.Y4qSZW_switch input:checked+span{background:var(--dsw-alias-state-business-primary)}.Y4qSZW_switch input:checked+span:after{transform:translate(16px)}.Y4qSZW_switch input:focus-visible+span{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.Y4qSZW_marker{display:none}[data-relay-simple-conversation=true] [role=tablist]{display:none}";
197
- const tagId$3 = "@agentrouter-top/relay-dsh-plugin-codex/AdvancedDebug.module.css";
198
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
196
+ const css$4 = ".Y4qSZW_section{width:100%;max-width:780px;color:var(--dsw-alias-label-primary)}.Y4qSZW_settingRow{border-bottom:1px solid var(--dsw-alias-border-l2);justify-content:space-between;align-items:center;gap:20px;min-height:58px;padding:8px 0;display:flex}.Y4qSZW_statusRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:flex-start;gap:10px;min-height:70px;padding:10px 0;display:flex}.Y4qSZW_statusDot{background:var(--dsw-alias-label-tertiary);border-radius:50%;flex:none;width:8px;height:8px;margin-top:6px}.Y4qSZW_statusRow[data-codex-status=connected] .Y4qSZW_statusDot{background:#27a65b}.Y4qSZW_statusRow[data-codex-status=connection-failed] .Y4qSZW_statusDot,.Y4qSZW_statusRow[data-codex-status=unavailable] .Y4qSZW_statusDot,.Y4qSZW_statusRow[data-codex-status=rebind-required] .Y4qSZW_statusDot{background:#d94841}.Y4qSZW_settingCopy code{color:var(--dsw-alias-label-tertiary);font-size:11px}.Y4qSZW_statusBadge{border-radius:999px;padding:2px 7px;font-size:11px;font-weight:600;line-height:16px}.Y4qSZW_statusError{color:#d94841;background:#d9484124}.Y4qSZW_statusRebind{color:#a95d00;background:#c26a0029}.Y4qSZW_settingCopy{flex-direction:column;gap:2px;min-width:0;display:flex}.Y4qSZW_settingCopy strong{font-size:14px;font-weight:500;line-height:20px}.Y4qSZW_settingCopy span{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.Y4qSZW_switch{cursor:pointer;flex:none;width:36px;height:20px;display:inline-flex;position:relative}.Y4qSZW_switch input{opacity:0;width:1px;height:1px;position:absolute}.Y4qSZW_switch span{background:var(--dsw-alias-fill-l2);border-radius:10px;width:100%;transition:background-color .12s}.Y4qSZW_switch span:after{background:var(--dsw-alias-bg-layer-1);content:\"\";border-radius:50%;width:16px;height:16px;transition:transform .12s;position:absolute;top:2px;left:2px;box-shadow:0 1px 3px #0003}.Y4qSZW_switch input:checked+span{background:var(--dsw-alias-state-business-primary)}.Y4qSZW_switch input:checked+span:after{transform:translate(16px)}.Y4qSZW_switch input:focus-visible+span{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:2px}.Y4qSZW_marker{display:none}[data-relay-simple-conversation=true] [role=tablist]{display:none}";
197
+ const tagId$4 = "@agentrouter-top/relay-dsh-plugin-codex/AdvancedDebug.module.css";
198
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$4) + "]") === null) {
199
199
  const tag = document.createElement("style");
200
200
  tag.dataset.plugin = "@agentrouter-top/relay-dsh-plugin-codex";
201
- tag.dataset.pluginCss = tagId$3;
202
- tag.textContent = css$3;
201
+ tag.dataset.pluginCss = tagId$4;
202
+ tag.textContent = css$4;
203
203
  document.head.appendChild(tag);
204
204
  }
205
205
  var AdvancedDebug_module_css_default = {
@@ -34889,6 +34889,10 @@ window.__ModuleLoader__.load({
34889
34889
  "exitCode",
34890
34890
  "commandActions"
34891
34891
  ]) if (activity[key] !== void 0 && typeof activity[key] !== "string") return null;
34892
+ if (activity.imageGeneration !== void 0) {
34893
+ const image = activity.imageGeneration;
34894
+ if (activity.type !== "imageGeneration" || !image || typeof image !== "object" || image.version !== 1 || image.attachmentId !== void 0 && (typeof image.attachmentId !== "string" || !image.attachmentId)) return null;
34895
+ }
34892
34896
  return value;
34893
34897
  }
34894
34898
  //#endregion
@@ -35213,13 +35217,13 @@ window.__ModuleLoader__.load({
35213
35217
  }
35214
35218
  //#endregion
35215
35219
  //#region \0relay-css-module:.\src\client\CodexActivityView.module.css.mjs
35216
- const css$2 = ".jIHzgW_activity{width:min(100%,960px);color:var(--dsw-alias-label-secondary)}.jIHzgW_row{gap:8px;min-width:0;max-width:100%}.jIHzgW_title{text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:0;font-size:14px;line-height:24px;overflow:hidden}.jIHzgW_summary{min-width:0;color:var(--dsw-alias-label-tertiary);flex:1;align-items:center;gap:8px;font-size:13px;line-height:20px;display:flex}.jIHzgW_summary :first-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.jIHzgW_detail{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-background-container,#f6f6f7);color:var(--dsw-alias-label-secondary);border-radius:8px;margin:8px 0 12px 28px;overflow:hidden}.jIHzgW_detailBlock{min-width:0}.jIHzgW_detailLabel{color:var(--dsw-alias-label-tertiary);padding:10px 12px 0;font-size:13px;line-height:18px}.jIHzgW_detail pre{max-height:360px;color:var(--dsw-alias-label-secondary);font:var(--dsw-font-markdown-code-block-small);white-space:pre-wrap;word-break:break-word;margin:0;padding:10px 12px 12px;overflow:auto}.jIHzgW_provenance{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;padding:10px 12px 0;font-size:12px;line-height:18px;overflow:hidden}.jIHzgW_footer{border-top:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);justify-content:space-between;gap:12px;padding:8px 12px;font-size:13px;line-height:18px;display:flex}";
35217
- const tagId$2 = "@agentrouter-top/relay-dsh-plugin-codex/CodexActivityView.module.css";
35218
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
35220
+ const css$3 = ".jIHzgW_activity{width:min(100%,960px);color:var(--dsw-alias-label-secondary)}.jIHzgW_row{gap:8px;min-width:0;max-width:100%}.jIHzgW_title{text-overflow:ellipsis;white-space:nowrap;flex:0 auto;min-width:0;font-size:14px;line-height:24px;overflow:hidden}.jIHzgW_summary{min-width:0;color:var(--dsw-alias-label-tertiary);flex:1;align-items:center;gap:8px;font-size:13px;line-height:20px;display:flex}.jIHzgW_summary :first-child{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.jIHzgW_detail{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-background-container,#f6f6f7);color:var(--dsw-alias-label-secondary);border-radius:8px;margin:8px 0 12px 28px;overflow:hidden}.jIHzgW_detailBlock{min-width:0}.jIHzgW_detailLabel{color:var(--dsw-alias-label-tertiary);padding:10px 12px 0;font-size:13px;line-height:18px}.jIHzgW_detail pre{max-height:360px;color:var(--dsw-alias-label-secondary);font:var(--dsw-font-markdown-code-block-small);white-space:pre-wrap;word-break:break-word;margin:0;padding:10px 12px 12px;overflow:auto}.jIHzgW_provenance{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;padding:10px 12px 0;font-size:12px;line-height:18px;overflow:hidden}.jIHzgW_footer{border-top:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-tertiary);justify-content:space-between;gap:12px;padding:8px 12px;font-size:13px;line-height:18px;display:flex}";
35221
+ const tagId$3 = "@agentrouter-top/relay-dsh-plugin-codex/CodexActivityView.module.css";
35222
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
35219
35223
  const tag = document.createElement("style");
35220
35224
  tag.dataset.plugin = "@agentrouter-top/relay-dsh-plugin-codex";
35221
- tag.dataset.pluginCss = tagId$2;
35222
- tag.textContent = css$2;
35225
+ tag.dataset.pluginCss = tagId$3;
35226
+ tag.textContent = css$3;
35223
35227
  document.head.appendChild(tag);
35224
35228
  }
35225
35229
  var CodexActivityView_module_css_default = {
@@ -35236,31 +35240,31 @@ window.__ModuleLoader__.load({
35236
35240
  //#endregion
35237
35241
  //#region src/client/process-presence.ts
35238
35242
  const mounted = /* @__PURE__ */ new Map();
35239
- const listeners = /* @__PURE__ */ new Set();
35240
- const subscribe = (listener) => {
35241
- listeners.add(listener);
35243
+ const listeners$1 = /* @__PURE__ */ new Set();
35244
+ const subscribe$1 = (listener) => {
35245
+ listeners$1.add(listener);
35242
35246
  return () => {
35243
- listeners.delete(listener);
35247
+ listeners$1.delete(listener);
35244
35248
  };
35245
35249
  };
35246
- const notify = () => {
35247
- for (const listener of listeners) listener();
35250
+ const notify$1 = () => {
35251
+ for (const listener of listeners$1) listener();
35248
35252
  };
35249
35253
  const keyOf = (sessionId, turn) => JSON.stringify([sessionId, turn]);
35250
35254
  /** Suppression is reversible and only active after the replacement mounts. */
35251
35255
  function mountProcess(sessionId, turn) {
35252
35256
  const key = keyOf(sessionId, turn);
35253
35257
  mounted.set(key, (mounted.get(key) ?? 0) + 1);
35254
- notify();
35258
+ notify$1();
35255
35259
  return () => {
35256
35260
  const count = (mounted.get(key) ?? 1) - 1;
35257
35261
  if (count === 0) mounted.delete(key);
35258
35262
  else mounted.set(key, count);
35259
- notify();
35263
+ notify$1();
35260
35264
  };
35261
35265
  }
35262
35266
  function useProcessPresence(sessionId, turn) {
35263
- return (0, react.useSyncExternalStore)(subscribe, () => mounted.has(keyOf(sessionId, turn)), () => false);
35267
+ return (0, react.useSyncExternalStore)(subscribe$1, () => mounted.has(keyOf(sessionId, turn)), () => false);
35264
35268
  }
35265
35269
  //#endregion
35266
35270
  //#region src/client/compatible-chat.ts
@@ -35722,6 +35726,24 @@ window.__ModuleLoader__.load({
35722
35726
  const last = text.at(-1);
35723
35727
  return last ? [last] : [];
35724
35728
  }
35729
+ /** Place each generated attachment in its original generation frame. Never guess
35730
+ * by filename, nearest activity, or cross-turn order. Legacy/MCP images stay native. */
35731
+ function imageGenerationFrames(segments) {
35732
+ const consumed = /* @__PURE__ */ new Set();
35733
+ const frames = /* @__PURE__ */ new Map();
35734
+ for (const segment of segments) {
35735
+ const generation = segment.activity?.imageGeneration;
35736
+ if (segment.kind !== "activity" || generation?.version !== 1) continue;
35737
+ const image = generation.attachmentId ? segments.find((candidate) => candidate.kind === "image" && candidate.step === segment.step && !consumed.has(candidate.key) && candidate.attachment?.attachmentId === generation.attachmentId) : void 0;
35738
+ if (image) consumed.add(image.key);
35739
+ frames.set(segment.key, {
35740
+ ...segment,
35741
+ kind: "image",
35742
+ attachment: image?.attachment
35743
+ });
35744
+ }
35745
+ return segments.filter((segment) => !consumed.has(segment.key)).map((segment) => frames.get(segment.key) ?? segment);
35746
+ }
35725
35747
  const codexProcessDefinition = {
35726
35748
  kind: "relay-codex-process",
35727
35749
  target: "chat",
@@ -35906,6 +35928,241 @@ window.__ModuleLoader__.load({
35906
35928
  return value.length > 15 ? `${value.slice(0, 8)}...${value.slice(-4)}` : value;
35907
35929
  }
35908
35930
  //#endregion
35931
+ //#region src/client/image-motion.ts
35932
+ const palette = [
35933
+ [
35934
+ 49,
35935
+ 190,
35936
+ 217
35937
+ ],
35938
+ [
35939
+ 88,
35940
+ 140,
35941
+ 246
35942
+ ],
35943
+ [
35944
+ 161,
35945
+ 113,
35946
+ 237
35947
+ ],
35948
+ [
35949
+ 237,
35950
+ 132,
35951
+ 188
35952
+ ],
35953
+ [
35954
+ 245,
35955
+ 189,
35956
+ 121
35957
+ ],
35958
+ [
35959
+ 78,
35960
+ 202,
35961
+ 214
35962
+ ]
35963
+ ];
35964
+ const listeners = /* @__PURE__ */ new Set();
35965
+ let reduced;
35966
+ const notify = () => {
35967
+ for (const listener of listeners) listener();
35968
+ };
35969
+ function subscribe(listener) {
35970
+ if (!listeners.size) {
35971
+ reduced = window.matchMedia?.("(prefers-reduced-motion: reduce)");
35972
+ reduced?.addEventListener("change", notify);
35973
+ document.addEventListener("visibilitychange", notify);
35974
+ }
35975
+ listeners.add(listener);
35976
+ return () => {
35977
+ listeners.delete(listener);
35978
+ if (!listeners.size) {
35979
+ reduced?.removeEventListener("change", notify);
35980
+ document.removeEventListener("visibilitychange", notify);
35981
+ reduced = void 0;
35982
+ }
35983
+ };
35984
+ }
35985
+ /** One bounded canvas timeline; no per-frame React state or host DOM inspection. */
35986
+ function mountImageMotion(canvas, dark) {
35987
+ const ctx = canvas.getContext("2d");
35988
+ if (!ctx) return () => {};
35989
+ let width = 0, height = 0, points = [];
35990
+ let clock = 1.6, last = 0, lastPaint = 0, raf = 0, disposed = false;
35991
+ let visible = typeof IntersectionObserver === "undefined";
35992
+ const colorAt = (position) => {
35993
+ const offset = (position % 1 + 1) % 1 * palette.length;
35994
+ const i = Math.floor(offset), f = offset - i;
35995
+ return palette[i].map((channel, index) => {
35996
+ const value = channel + (palette[(i + 1) % palette.length][index] - channel) * f;
35997
+ return Math.round(dark ? value + (255 - value) * .16 : value);
35998
+ }).join(",");
35999
+ };
36000
+ const draw = () => {
36001
+ if (!width || !height) return;
36002
+ ctx.clearRect(0, 0, width, height);
36003
+ const t = clock;
36004
+ for (let i = 0; i < 3; i++) {
36005
+ const x = width * (.25 + i * .24 + .045 * Math.sin(t * .43 + i));
36006
+ const y = height * (.43 + .075 * Math.sin(t * .6 + i));
36007
+ const glow = ctx.createRadialGradient(x, y, 0, x, y, width * .31);
36008
+ const rgb = colorAt(i * .28 - t * .045);
36009
+ glow.addColorStop(0, `rgba(${rgb},${dark ? .105 : .066})`);
36010
+ glow.addColorStop(1, `rgba(${rgb},0)`);
36011
+ ctx.fillStyle = glow;
36012
+ ctx.fillRect(0, 0, width, height);
36013
+ }
36014
+ for (const p of points) {
36015
+ if (p.edge < .002) continue;
36016
+ const u = p.nx * .93 + p.ny * .24, v = p.ny * .95 - p.nx * .22;
36017
+ const spine = .2 * Math.sin(u * 3.3 - t * .8) + .14 * Math.sin(t * .42);
36018
+ const main = Math.exp(-Math.pow((v - spine) / .21, 2));
36019
+ const echo = Math.exp(-Math.pow((v - spine + .43) / .29, 2));
36020
+ const travel = .59 + .41 * Math.sin(u * 4.2 - t * 1.12 + p.ny * .7);
36021
+ const swell = .8 + .2 * Math.sin(t * .62 + u * 1.9);
36022
+ const energy = (main * (.7 + .3 * travel) + echo * .3) * swell;
36023
+ const alpha = (.022 + energy * (dark ? .86 : .79)) * p.edge;
36024
+ const radius = .53 + energy * .9;
36025
+ const dx = Math.sin(p.ny * 4.1 + t * .61) * .7 * energy;
36026
+ const dy = Math.cos(p.nx * 3.2 - t * .5) * 1.02 * energy;
36027
+ ctx.fillStyle = `rgba(${colorAt((u + 1) * .39 + p.ny * .08 - t * .045)},${alpha.toFixed(3)})`;
36028
+ ctx.beginPath();
36029
+ ctx.arc(p.x + dx, p.y + dy, radius, 0, Math.PI * 2);
36030
+ ctx.fill();
36031
+ if (energy > .6) {
36032
+ ctx.fillStyle = `rgba(255,255,255,${((energy - .6) * p.edge * (dark ? .65 : .32)).toFixed(3)})`;
36033
+ ctx.beginPath();
36034
+ ctx.arc(p.x + dx, p.y + dy, radius * .4, 0, Math.PI * 2);
36035
+ ctx.fill();
36036
+ }
36037
+ }
36038
+ };
36039
+ const canAnimate = () => !disposed && visible && width > 0 && height > 0 && !document.hidden && !reduced?.matches;
36040
+ const tick = (now) => {
36041
+ raf = 0;
36042
+ if (!canAnimate()) {
36043
+ last = 0;
36044
+ return;
36045
+ }
36046
+ if (last) clock += Math.min((now - last) / 1e3, .07);
36047
+ last = now;
36048
+ if (now - lastPaint >= 30) {
36049
+ draw();
36050
+ lastPaint = now;
36051
+ }
36052
+ raf = requestAnimationFrame(tick);
36053
+ };
36054
+ const sync = () => {
36055
+ cancelAnimationFrame(raf);
36056
+ raf = 0;
36057
+ last = 0;
36058
+ if (canAnimate()) raf = requestAnimationFrame(tick);
36059
+ };
36060
+ const resize = () => {
36061
+ if (disposed) return;
36062
+ const rect = canvas.getBoundingClientRect(), ratio = Math.min(window.devicePixelRatio || 1, 2);
36063
+ width = rect.width;
36064
+ height = rect.height;
36065
+ canvas.width = Math.round(width * ratio);
36066
+ canvas.height = Math.round(height * ratio);
36067
+ ctx.setTransform(ratio, 0, 0, ratio, 0, 0);
36068
+ const spacing = width < 400 ? 7.4 : 8.4, inset = 22;
36069
+ points = [];
36070
+ for (let y = inset; y < height - 48; y += spacing) for (let x = inset; x < width - inset; x += spacing) {
36071
+ const nx = (x - width * .5) / (width * .47), ny = (y - height * .43) / (height * .42);
36072
+ points.push({
36073
+ x,
36074
+ y,
36075
+ nx,
36076
+ ny,
36077
+ edge: Math.pow(Math.max(0, 1 - nx ** 4 - ny ** 4), 1.2)
36078
+ });
36079
+ }
36080
+ draw();
36081
+ sync();
36082
+ };
36083
+ const unsubscribe = subscribe(sync);
36084
+ const resizer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(resize);
36085
+ const intersection = typeof IntersectionObserver === "undefined" ? void 0 : new IntersectionObserver((entries) => {
36086
+ if (disposed) return;
36087
+ visible = entries.some((entry) => entry.isIntersecting);
36088
+ sync();
36089
+ });
36090
+ resizer?.observe(canvas);
36091
+ intersection?.observe(canvas);
36092
+ resize();
36093
+ return () => {
36094
+ disposed = true;
36095
+ cancelAnimationFrame(raf);
36096
+ unsubscribe();
36097
+ resizer?.disconnect();
36098
+ intersection?.disconnect();
36099
+ };
36100
+ }
36101
+ //#endregion
36102
+ //#region \0relay-css-module:.\src\client\CodexImageGeneration.module.css.mjs
36103
+ const css$2 = ".gSdKhG_frame{--image-well:#f7f8ff;--image-accent:#6f77a7;border-radius:24px;width:min(100%,480px);min-width:0;position:relative;overflow:hidden}.gSdKhG_frame[data-color-scheme=dark]{--image-well:#1a2238;--image-accent:#b9c5fa}.gSdKhG_placeholder{box-sizing:border-box;aspect-ratio:4/3;background:var(--image-well);color:var(--image-accent);border:1px solid var(--dsw-alias-border-l2,#e6e8f2);border-radius:inherit;place-items:end center;transition:opacity 1.2s,filter 1.2s,transform 1.6s,visibility 0s 1.2s;display:grid;position:relative;overflow:hidden}.gSdKhG_field{pointer-events:none;width:100%;height:100%;position:absolute;inset:0}.gSdKhG_label{z-index:1;text-align:center;gap:6px;margin:0 16px 26px;font-size:13px;line-height:20px;display:grid}.gSdKhG_subtitle{opacity:.75;font-size:11px}.gSdKhG_result{min-width:0}.gSdKhG_frame[data-state=ready] .gSdKhG_placeholder{aspect-ratio:auto;opacity:0;filter:blur(5px);visibility:hidden;pointer-events:none;position:absolute;inset:0;transform:scale(1.025)}.gSdKhG_frame[data-reveal] .gSdKhG_result{animation:1.6s both gSdKhG_codex-image-reveal}.gSdKhG_frame[data-state=stopped] .gSdKhG_field{opacity:.4}@keyframes gSdKhG_codex-image-reveal{0%{opacity:0;transform:scale(1.035)}to{opacity:1;transform:scale(1)}}@media (prefers-reduced-motion:reduce){.gSdKhG_placeholder{transition:none}.gSdKhG_frame[data-reveal] .gSdKhG_result{animation:none}}";
36104
+ const tagId$2 = "@agentrouter-top/relay-dsh-plugin-codex/CodexImageGeneration.module.css";
36105
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
36106
+ const tag = document.createElement("style");
36107
+ tag.dataset.plugin = "@agentrouter-top/relay-dsh-plugin-codex";
36108
+ tag.dataset.pluginCss = tagId$2;
36109
+ tag.textContent = css$2;
36110
+ document.head.appendChild(tag);
36111
+ }
36112
+ var CodexImageGeneration_module_css_default = {
36113
+ "codex-image-reveal": "gSdKhG_codex-image-reveal",
36114
+ "field": "gSdKhG_field",
36115
+ "frame": "gSdKhG_frame",
36116
+ "label": "gSdKhG_label",
36117
+ "placeholder": "gSdKhG_placeholder",
36118
+ "result": "gSdKhG_result",
36119
+ "subtitle": "gSdKhG_subtitle"
36120
+ };
36121
+ //#endregion
36122
+ //#region src/client/CodexImageGeneration.tsx
36123
+ const ImageColorScheme = (0, react.createContext)("light");
36124
+ function CodexImageGeneration({ segment, running, renderMessageImages }) {
36125
+ const pending = !segment.attachment && running && segment.activity?.status !== "error";
36126
+ const scheme = (0, react.useContext)(ImageColorScheme);
36127
+ const canvas = (0, react.useRef)(null);
36128
+ const beganWithPlaceholder = (0, react.useRef)(!segment.attachment).current;
36129
+ (0, react.useEffect)(() => {
36130
+ if (pending && canvas.current) return mountImageMotion(canvas.current, scheme === "dark");
36131
+ }, [pending, scheme]);
36132
+ const label = pending ? "正在生成图片" : "图片生成已结束,未提供可显示的图片";
36133
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
36134
+ className: CodexImageGeneration_module_css_default.frame,
36135
+ "data-codex-image-generation": segment.key,
36136
+ "data-color-scheme": scheme,
36137
+ "data-reveal": beganWithPlaceholder && Boolean(segment.attachment) || void 0,
36138
+ "data-state": segment.attachment ? "ready" : pending ? "generating" : "stopped",
36139
+ "aria-busy": pending,
36140
+ children: [segment.attachment && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36141
+ className: CodexImageGeneration_module_css_default.result,
36142
+ children: renderMessageImages({
36143
+ images: [{ attachment: segment.attachment }],
36144
+ align: "start"
36145
+ })
36146
+ }), beganWithPlaceholder && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
36147
+ className: CodexImageGeneration_module_css_default.placeholder,
36148
+ "aria-hidden": Boolean(segment.attachment) || void 0,
36149
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("canvas", {
36150
+ ref: canvas,
36151
+ className: CodexImageGeneration_module_css_default.field,
36152
+ "aria-hidden": "true"
36153
+ }), !segment.attachment && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
36154
+ className: CodexImageGeneration_module_css_default.label,
36155
+ role: "status",
36156
+ "aria-live": "polite",
36157
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }), pending && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
36158
+ className: CodexImageGeneration_module_css_default.subtitle,
36159
+ children: "图像即将在这里呈现"
36160
+ })]
36161
+ })]
36162
+ })]
36163
+ });
36164
+ }
36165
+ //#endregion
35909
36166
  //#region src/client/process-files.ts
35910
36167
  function codexProducedFileMentions(segments, openFile, native) {
35911
36168
  const paths = /* @__PURE__ */ new Set();
@@ -35953,7 +36210,7 @@ window.__ModuleLoader__.load({
35953
36210
  }
35954
36211
  //#endregion
35955
36212
  //#region \0relay-css-module:.\src\client\CodexProcessView.module.css.mjs
35956
- const css$1 = "._06p8sG_process{width:100%;min-width:0;color:var(--dsw-alias-label-secondary)}._06p8sG_processToggle,._06p8sG_groupToggle{min-width:0;max-width:100%;min-height:28px;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:8px;padding:2px 0;font-size:14px;line-height:24px;display:flex}._06p8sG_processToggle{margin-bottom:16px}._06p8sG_processToggle:focus-visible,._06p8sG_groupToggle:focus-visible{outline:2px solid var(--dsw-static-deepseek-500,#416aff);outline-offset:3px;border-radius:4px}._06p8sG_groupToggle>svg{flex:none}._06p8sG_groupTitle{white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}._06p8sG_count{flex-shrink:0;font-size:12px}._06p8sG_flow,._06p8sG_answer{flex-direction:column;gap:20px;min-width:0;display:flex}._06p8sG_flow[hidden]{display:none}._06p8sG_flow{border-top:1px solid var(--dsw-alias-border-l2);padding-top:16px}._06p8sG_answer{color:var(--dsw-alias-label-primary)}._06p8sG_flow:not([hidden])+._06p8sG_answer{margin-top:24px}._06p8sG_prose{min-width:0;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere}._06p8sG_prose p{margin:0 0 12px}._06p8sG_prose p:last-child{margin-bottom:0}._06p8sG_activities{flex-direction:column;gap:8px;min-width:0;padding:8px 0 0 20px;display:flex}._06p8sG_reasoning{overflow-wrap:anywhere;margin:8px 0 0 24px;font-size:13px}._06p8sG_error{color:var(--dsw-alias-state-error-primary,#b42318)}._06p8sG_spinner{animation:1.4s linear infinite _06p8sG_codex-spin}@keyframes _06p8sG_codex-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){._06p8sG_spinner{animation:none}}[data-chat-flow-kind=assistant-step]:has(>[data-slot]>[data-codex-native-assistant]){display:none}[data-chat-flow-kind=tool-call]:has(>[data-slot]>[data-chat-call-id]>[data-slot]>[data-codex-grouped-call]){display:none}[data-chat-flow-kind=context]:has(>[data-slot]>[data-codex-internal-context]){display:none}[data-chat-flow-kind=relay-codex-process]:has(>[data-slot]>[data-codex-process-fallback]){display:none}[data-chat-flow]:has([data-codex-process-turn][data-status=running])>[role=status][aria-live=polite]{display:none}";
36213
+ const css$1 = "._06p8sG_process{width:100%;min-width:0;color:var(--dsw-alias-label-secondary)}._06p8sG_processToggle,._06p8sG_groupToggle{min-width:0;max-width:100%;min-height:28px;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;gap:8px;padding:2px 0;font-size:14px;line-height:24px;display:flex}._06p8sG_processToggle{margin-bottom:16px}._06p8sG_processToggle:focus-visible,._06p8sG_groupToggle:focus-visible{outline:2px solid var(--dsw-static-deepseek-500,#416aff);outline-offset:3px;border-radius:4px}._06p8sG_groupToggle>svg{flex:none}._06p8sG_groupTitle{white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}._06p8sG_count{flex-shrink:0;font-size:12px}._06p8sG_flow,._06p8sG_answer{flex-direction:column;gap:20px;min-width:0;display:flex}._06p8sG_flow[hidden]{display:none}._06p8sG_flow{border-top:1px solid var(--dsw-alias-border-l2);padding-top:16px}._06p8sG_flow[data-images-only]{border-top:0;padding-top:0}._06p8sG_flow>[hidden]{display:none}._06p8sG_answer{color:var(--dsw-alias-label-primary)}._06p8sG_flow:not([hidden])+._06p8sG_answer{margin-top:24px}._06p8sG_prose{min-width:0;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere}._06p8sG_prose p{margin:0 0 12px}._06p8sG_prose p:last-child{margin-bottom:0}._06p8sG_activities{flex-direction:column;gap:8px;min-width:0;padding:8px 0 0 20px;display:flex}._06p8sG_reasoning{overflow-wrap:anywhere;margin:8px 0 0 24px;font-size:13px}._06p8sG_error{color:var(--dsw-alias-state-error-primary,#b42318)}._06p8sG_spinner{animation:1.4s linear infinite _06p8sG_codex-spin}@keyframes _06p8sG_codex-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){._06p8sG_spinner{animation:none}}[data-chat-flow-kind=assistant-step]:has(>[data-slot]>[data-codex-native-assistant]){display:none}[data-chat-flow-kind=tool-call]:has(>[data-slot]>[data-chat-call-id]>[data-slot]>[data-codex-grouped-call]){display:none}[data-chat-flow-kind=context]:has(>[data-slot]>[data-codex-internal-context]){display:none}[data-chat-flow-kind=relay-codex-process]:has(>[data-slot]>[data-codex-process-fallback]){display:none}[data-chat-flow]:has([data-codex-process-turn][data-status=running])>[role=status][aria-live=polite]{display:none}";
35957
36214
  const tagId$1 = "@agentrouter-top/relay-dsh-plugin-codex/CodexProcessView.module.css";
35958
36215
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
35959
36216
  const tag = document.createElement("style");
@@ -36058,13 +36315,15 @@ window.__ModuleLoader__.load({
36058
36315
  };
36059
36316
  }, [state.status]);
36060
36317
  const answerKeys = new Set(codexProcessAnswerSegments(state).map((segment) => segment.key));
36061
- const finalIndex = state.segments.findIndex((segment) => answerKeys.has(segment.key));
36062
- const process = finalIndex < 0 ? state.segments : state.segments.slice(0, finalIndex);
36063
- const answer = finalIndex < 0 ? [] : state.segments.slice(finalIndex);
36318
+ const segments = imageGenerationFrames(state.segments);
36319
+ const finalIndex = segments.findIndex((segment) => answerKeys.has(segment.key));
36320
+ const process = finalIndex < 0 ? segments : segments.slice(0, finalIndex);
36321
+ const answer = finalIndex < 0 ? [] : segments.slice(finalIndex);
36064
36322
  const deliverables = process.filter((segment) => segment.kind === "image");
36323
+ const persistentImages = deliverables.some((segment) => segment.activity?.imageGeneration);
36065
36324
  const hasProcess = process.some((segment) => segment.kind !== "text" || Boolean(segment.text?.trim()));
36066
36325
  const open = expanded ?? (state.status === "running" || answer.length === 0 && deliverables.length === 0);
36067
- const visibleAnswer = [...open ? [] : deliverables, ...answer];
36326
+ const visibleAnswer = [...open || persistentImages ? [] : deliverables, ...answer];
36068
36327
  const seconds = Math.max(0, Math.floor(((state.endedAt ?? now) - state.startedAt) / 1e3));
36069
36328
  const elapsed = seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
36070
36329
  const status = state.status === "running" ? "Working" : state.status === "error" ? "Stopped" : "Worked";
@@ -36086,22 +36345,23 @@ window.__ModuleLoader__.load({
36086
36345
  ] }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(import_lucide_react.ChevronDown, { size: 15 }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(import_lucide_react.ChevronRight, { size: 15 })]
36087
36346
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
36088
36347
  className: CodexProcessView_module_css_default.flow,
36089
- hidden: !open,
36348
+ hidden: !open && !persistentImages,
36090
36349
  "data-codex-process-content": true,
36091
- children: [
36092
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProcessItems, {
36093
- segments: process,
36094
- renderMessageImages,
36095
- running: state.status === "running",
36096
- showImages: open
36097
- }),
36098
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningSummary, { segments: process }),
36099
- state.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36350
+ "data-images-only": !open && persistentImages || void 0,
36351
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProcessItems, {
36352
+ segments: process,
36353
+ renderMessageImages,
36354
+ running: state.status === "running",
36355
+ showImages: open || persistentImages,
36356
+ collapseProcess: !open
36357
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
36358
+ hidden: !open,
36359
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReasoningSummary, { segments: process }), state.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36100
36360
  className: CodexProcessView_module_css_default.error,
36101
36361
  role: "status",
36102
36362
  children: state.error
36103
- })
36104
- ]
36363
+ })]
36364
+ })]
36105
36365
  })] }), visibleAnswer.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36106
36366
  className: CodexProcessView_module_css_default.answer,
36107
36367
  "data-codex-final-answer": true,
@@ -36114,11 +36374,19 @@ window.__ModuleLoader__.load({
36114
36374
  })]
36115
36375
  });
36116
36376
  }
36117
- function ProcessItems({ segments, renderMessageImages, running, fileMentions, showImages = true }) {
36118
- return groupProcessSegments(segments).map((item) => "activities" in item ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActivityGroup, { segments: item.activities }, item.key) : item.kind === "image" && item.attachment ? showImages ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderMessageImages({
36377
+ function ProcessItems({ segments, renderMessageImages, running, fileMentions, showImages = true, collapseProcess = false }) {
36378
+ return groupProcessSegments(segments).map((item) => "activities" in item ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36379
+ hidden: collapseProcess,
36380
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActivityGroup, { segments: item.activities })
36381
+ }, item.key) : item.activity?.imageGeneration ? showImages ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexImageGeneration, {
36382
+ segment: item,
36383
+ running,
36384
+ renderMessageImages
36385
+ }, item.key) : null : item.kind === "image" && item.attachment ? showImages ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: renderMessageImages({
36119
36386
  images: [{ attachment: item.attachment }],
36120
36387
  align: "start"
36121
36388
  }) }, item.key) : null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
36389
+ hidden: collapseProcess,
36122
36390
  className: CodexProcessView_module_css_default.prose,
36123
36391
  "data-codex-commentary": item.phase !== "final_answer" || void 0,
36124
36392
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.MarkdownText, {
@@ -36221,10 +36489,18 @@ window.__ModuleLoader__.load({
36221
36489
  conversationEvents(ctx).register(codexProcessDefinition);
36222
36490
  installContextPresentation(ctx, debug);
36223
36491
  ctx.slots.inject("conversation.chat.node", () => {
36492
+ const subscribeTheme = (listener) => ctx.on("theme/change", listener);
36493
+ const getScheme = () => ctx.theme.getTheme().active.colorScheme;
36224
36494
  const disposeProcess = ctx.slots.register({
36225
36495
  name: "conversation.chat.node",
36226
36496
  key: "relay-codex-process"
36227
- }, CodexProcessView);
36497
+ }, function ThemedCodexProcess(props) {
36498
+ const scheme = (0, react.useSyncExternalStore)(subscribeTheme, getScheme, getScheme);
36499
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageColorScheme.Provider, {
36500
+ value: scheme,
36501
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexProcessView, { ...props })
36502
+ });
36503
+ });
36228
36504
  let previous;
36229
36505
  let disposeAssistant;
36230
36506
  const reconcile = () => {