@oh-story/dsh 0.1.6 → 0.1.8

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.
Files changed (41) hide show
  1. package/README.md +29 -12
  2. package/lib/client.js +356 -89
  3. package/lib/client.js.map +4 -4
  4. package/lib/drama/manifest.json +34 -34
  5. package/lib/drama/skills/short-drama/SKILL.md +4 -1
  6. package/lib/drama/skills/short-drama/references/contract-and-ownership.md +2 -0
  7. package/lib/drama/skills/short-drama/references/creator-documents.md +30 -4
  8. package/lib/drama/skills/short-drama/references/creator-workflow.md +8 -2
  9. package/lib/drama/skills/short-drama/references/reference-roles.md +16 -2
  10. package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +328 -48
  11. package/lib/drama/skills/short-drama-produce/SKILL.md +4 -1
  12. package/lib/drama/skills/short-drama-produce/scripts/production_tool.py +34 -2
  13. package/lib/drama/skills/short-drama-storyboard/SKILL.md +27 -5
  14. package/lib/drama/skills/short-drama-storyboard/references/review-and-fixtures.md +2 -1
  15. package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +2 -2
  16. package/lib/drama/skills/short-drama-video-prompts/SKILL.md +11 -2
  17. package/lib/drama/skills/short-drama-video-prompts/assets/video-prompts.md +6 -1
  18. package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +19 -3
  19. package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +8 -2
  20. package/lib/drama/skills/short-drama-video-prompts/references/target-model-profile.md +1 -0
  21. package/lib/index.js +194 -62
  22. package/lib/index.js.map +4 -4
  23. package/lib/types/client/drama-production-view.d.ts +1 -0
  24. package/lib/types/client/drama-production-view.d.ts.map +1 -1
  25. package/lib/types/client/drama-production.d.ts +3 -0
  26. package/lib/types/client/drama-production.d.ts.map +1 -1
  27. package/lib/types/client/index.d.ts.map +1 -1
  28. package/lib/types/client/video-studio.d.ts +2 -1
  29. package/lib/types/client/video-studio.d.ts.map +1 -1
  30. package/lib/types/client/workbench-presence.d.ts +29 -0
  31. package/lib/types/client/workbench-presence.d.ts.map +1 -0
  32. package/lib/types/drama-adapters.d.ts +65 -0
  33. package/lib/types/drama-adapters.d.ts.map +1 -0
  34. package/lib/types/host-python.d.ts +19 -0
  35. package/lib/types/host-python.d.ts.map +1 -0
  36. package/lib/types/index.d.ts +1 -0
  37. package/lib/types/index.d.ts.map +1 -1
  38. package/lib/types/skill-provider.d.ts +1 -1
  39. package/lib/types/skill-provider.d.ts.map +1 -1
  40. package/lib/types/workspace-route.d.ts.map +1 -1
  41. package/package.json +41 -41
package/lib/client.js CHANGED
@@ -560,10 +560,11 @@ function parseEpisodeProduction(documents, episodeDirectory) {
560
560
  let storyboardPath = `${episodeDirectory}/\u5206\u955C.md`, imagePromptPath = `${episodeDirectory}/\u56FE\u7247\u63D0\u793A\u8BCD.md`, videoPromptPath = `${episodeDirectory}/\u89C6\u9891\u63D0\u793A\u8BCD.md`, visualPath = `${episodeDirectory}/\u89C6\u89C9\u8BBE\u5B9A.md`, shots = parseStoryboard(storyboardPath, documents[storyboardPath] ?? ""), assets = parseImagePrompts(imagePromptPath, documents[imagePromptPath] ?? ""), motions = parseVideoPrompts(videoPromptPath, documents[videoPromptPath] ?? ""), visualAssets = parseVisualAssets(visualPath, documents[visualPath] ?? ""), motionByShot = new Map(motions.flatMap((motion) => motion.shotId === void 0 ? [] : [[motion.shotId, motion]])), linkedShots = shots.map((shot) => ({ ...shot, motion: motionByShot.get(shot.id) })), targets = /* @__PURE__ */ new Map();
561
561
  for (let item of [...linkedShots, ...assets, ...motions, ...visualAssets])
562
562
  targets.set(item.id, { path: item.path, offset: item.offset, id: item.id });
563
+ let screenplayPath = `${episodeDirectory}/\u5267\u672C.md`;
563
564
  for (let shot of linkedShots)
564
- if (shot.source !== void 0) {
565
- let screenplayPath = `${episodeDirectory}/\u5267\u672C.md`, target = sectionTarget(screenplayPath, documents[screenplayPath] ?? "", shot.source);
566
- target !== void 0 && targets.set(shot.source, target);
565
+ for (let id of sourceKeys(shot)) {
566
+ let target = sectionTarget(screenplayPath, documents[screenplayPath] ?? "", id);
567
+ target !== void 0 && targets.set(id, target);
567
568
  }
568
569
  let diagnostics = validateProductionProtocol({
569
570
  documents,
@@ -589,13 +590,14 @@ function parseStoryboard(path, content) {
589
590
  return levelTwoSections(content).flatMap((section) => {
590
591
  let match = /^(SHOT-[A-Z0-9-]+)\s*(?:[·||]\s*)?(.*)$/iu.exec(section.heading.trim());
591
592
  if (match === null || match[1] === void 0) return [];
592
- let fields = bulletFields(section.body), id = match[1].toLocaleUpperCase();
593
+ let fields = bulletFields(section.body), id = match[1].toLocaleUpperCase(), source = firstField(fields, "\u6765\u6E90", "\u573A\u6B21");
593
594
  return [{
594
595
  id,
595
596
  title: match[2]?.trim() || id,
596
597
  path,
597
598
  offset: section.offset,
598
- source: firstField(fields, "\u6765\u6E90", "\u573A\u6B21"),
599
+ source,
600
+ sceneIds: sourceSceneIds(source),
599
601
  durationSeconds: seconds(firstField(fields, "\u65F6\u957F")),
600
602
  purpose: firstField(fields, "\u76EE\u7684", "\u955C\u5934\u76EE\u7684"),
601
603
  shotSpec: firstField(fields, "\u666F\u522B/\u673A\u4F4D", "\u666F\u522B", "\u955C\u5934\u89C4\u683C"),
@@ -663,7 +665,8 @@ function validateProductionProtocol(input) {
663
665
  for (let shot of input.shots) {
664
666
  for (let reference of shot.references)
665
667
  knownReferences.has(reference) || add({ severity: "warning", code: "unknown_reference", path: shot.path, offset: shot.offset, targetId: shot.id, message: `${shot.id} \u5F15\u7528\u4E86\u672A\u89E3\u6790\u7684 ${reference}\u3002` });
666
- shot.source !== void 0 && sectionTarget(screenplayPath, input.documents[screenplayPath] ?? "", shot.source) === void 0 && add({ severity: "warning", code: "unknown_source", path: shot.path, offset: shot.offset, targetId: shot.id, message: `${shot.id} \u7684\u6765\u6E90 ${shot.source} \u5728\u5267\u672C\u4E2D\u4E0D\u5B58\u5728\u3002` });
668
+ for (let id of sourceKeys(shot))
669
+ sectionTarget(screenplayPath, input.documents[screenplayPath] ?? "", id) === void 0 && add({ severity: "warning", code: "unknown_source", path: shot.path, offset: shot.offset, targetId: shot.id, message: `${shot.id} \u7684\u6765\u6E90 ${id} \u5728\u5267\u672C\u4E2D\u4E0D\u5B58\u5728\u3002` });
667
670
  }
668
671
  let shots = new Set(input.shots.map((shot) => shot.id)), motionsByShot = /* @__PURE__ */ new Map();
669
672
  for (let motion of input.motions) {
@@ -739,6 +742,12 @@ function slug(value) {
739
742
  function lineAt(content, offset) {
740
743
  return content.slice(0, Math.max(0, offset)).split(/\r?\n/u).length;
741
744
  }
745
+ function sourceSceneIds(source) {
746
+ return source === void 0 ? [] : [...new Set([...source.matchAll(/(?:[A-Za-z0-9]+-)+SC[0-9]+/gu)].map((match) => match[0]))];
747
+ }
748
+ function sourceKeys(shot) {
749
+ return shot.sceneIds.length > 0 ? shot.sceneIds : shot.source === void 0 ? [] : [shot.source];
750
+ }
742
751
  function sectionTarget(path, content, id) {
743
752
  let offset = content.search(new RegExp(`^##\\s+${id.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}(?:\\s|$)`, "imu"));
744
753
  return offset < 0 ? void 0 : { path, offset, id };
@@ -751,6 +760,19 @@ function creatorDocumentOrder(path) {
751
760
  // src/client/drama-production-view.tsx
752
761
  var import_react = require("react");
753
762
 
763
+ // src/client/workbench-ui.ts
764
+ function handleTabKey(event, values, current, select) {
765
+ let index;
766
+ if (event.key === "Home" ? index = 0 : event.key === "End" ? index = values.length - 1 : event.key === "ArrowRight" ? index = (values.indexOf(current) + 1) % values.length : event.key === "ArrowLeft" && (index = (values.indexOf(current) - 1 + values.length) % values.length), index === void 0) return;
767
+ event.preventDefault();
768
+ let value = values[index];
769
+ value !== void 0 && (select(value), event.currentTarget.parentElement?.querySelectorAll("[role='tab']")[index]?.focus());
770
+ }
771
+ function endpoint(path, sessionId, file) {
772
+ let url = new URL(`/oh-story/${path}`, globalThis.location.origin);
773
+ return url.searchParams.set("sessionId", sessionId), file !== void 0 && url.searchParams.set("path", file), url.toString();
774
+ }
775
+
754
776
  // src/client/production-prompts.ts
755
777
  var authorityBoundary = "\u53EA\u4F7F\u7528\u5F53\u524D DSH Preset \u53EF\u89C1\u7684\u5DE5\u5177\uFF1B\u6240\u6709\u6587\u4EF6\u3001\u7F51\u7EDC\u3001\u751F\u6210\u548C\u547D\u4EE4\u64CD\u4F5C\u7EE7\u7EED\u9075\u5B88 DSH \u6743\u9650\u4E0E\u5BA1\u6279\u3002";
756
778
  function nativeProductionPrompt(production, job, references) {
@@ -905,7 +927,53 @@ function reorderSequence(sequence, source, target) {
905
927
  }
906
928
 
907
929
  // src/client/drama-production-view.tsx
908
- var import_jsx_runtime3 = require("react/jsx-runtime"), SECTION_LABELS = { shots: "\u955C\u5934", assets: "\u7D20\u6750", tasks: "\u4EFB\u52A1", sequence: "\u6210\u7247", canvas: "\u753B\u5E03" }, SECTION_ORDER = Object.keys(SECTION_LABELS), STATUS_LABELS = { awaiting_confirmation: "\u7B49\u5F85\u786E\u8BA4", pending: "\u5DF2\u63D0\u4EA4", running: "DSH \u6267\u884C\u4E2D", dispatched_unknown: "\u5F85\u6838\u5BF9", succeeded: "\u5DF2\u5B8C\u6210", failed: "\u5931\u8D25", canceled: "\u5DF2\u53D6\u6D88" }, ASSET_KIND_LABEL = { character: "\u4EBA\u7269", scene: "\u573A\u666F", prop: "\u9053\u5177", state: "\u72B6\u6001", unknown: "\u8BBE\u5B9A" }, JOB_KIND_LABEL = { image: "\u56FE\u7247", video: "\u89C6\u9891", composition: "\u6210\u7247" };
930
+ var import_jsx_runtime3 = require("react/jsx-runtime"), SECTION_LABELS = { shots: "\u955C\u5934", assets: "\u7D20\u6750", tasks: "\u4EFB\u52A1", sequence: "\u6210\u7247", canvas: "\u753B\u5E03" }, SECTION_ORDER = Object.keys(SECTION_LABELS), STATUS_LABELS = { awaiting_confirmation: "\u7B49\u5F85\u786E\u8BA4", pending: "\u5DF2\u63D0\u4EA4", running: "DSH \u6267\u884C\u4E2D", dispatched_unknown: "\u5F85\u6838\u5BF9", succeeded: "\u5DF2\u5B8C\u6210", failed: "\u5931\u8D25", canceled: "\u5DF2\u53D6\u6D88" }, ASSET_KIND_LABEL = { character: "\u4EBA\u7269", scene: "\u573A\u666F", prop: "\u9053\u5177", state: "\u72B6\u6001", unknown: "\u8BBE\u5B9A" }, JOB_KIND_LABEL = { image: "\u56FE\u7247", video: "\u89C6\u9891", composition: "\u6210\u7247" }, MODALITY_LABEL = { image: "\u56FE\u7247", video: "\u89C6\u9891", music: "\u97F3\u4E50" };
931
+ function ProductionEnvironment({ sessionId }) {
932
+ let [preflight, setPreflight] = (0, import_react.useState)(), [attempt, setAttempt] = (0, import_react.useState)(0);
933
+ (0, import_react.useEffect)(() => {
934
+ let controller = new AbortController();
935
+ return fetch(endpoint("drama-preflight", sessionId), { signal: controller.signal }).then(async (response) => {
936
+ if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
937
+ let summary = await response.json();
938
+ controller.signal.aborted || setPreflight(summary);
939
+ }).catch(() => {
940
+ controller.signal.aborted || setPreflight("failed");
941
+ }), () => {
942
+ controller.abort();
943
+ };
944
+ }, [attempt, sessionId]);
945
+ let unconfigured = typeof preflight == "object" ? preflight.adapters.filter((adapter) => !adapter.configured) : [];
946
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "oh-story-production-environment", role: "status", "aria-label": "\u5A92\u4F53\u751F\u6210\u73AF\u5883", children: [
947
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: "\u751F\u6210\u73AF\u5883" }),
948
+ preflight === void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "data-pending": !0, children: "\u68C0\u67E5\u4E2D\u2026" }),
949
+ preflight === "failed" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: () => {
950
+ setPreflight(void 0), setAttempt((value) => value + 1);
951
+ }, children: "\u73AF\u5883\u68C0\u67E5\u5931\u8D25 \xB7 \u91CD\u8BD5" }),
952
+ typeof preflight == "object" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
953
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { "data-ready": preflight.python.ok || void 0, children: [
954
+ "Python ",
955
+ preflight.python.version ?? "\u672A\u627E\u5230"
956
+ ] }),
957
+ preflight.adapters.map((adapter) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { "data-ready": adapter.configured || void 0, title: adapter.configured ? `${adapter.name} \u5DF2\u914D\u7F6E` : `\u7F3A\u5C11\u73AF\u5883\u53D8\u91CF ${adapter.missing.join("\u3001")}`, children: [
958
+ MODALITY_LABEL[adapter.modality],
959
+ " ",
960
+ adapter.label,
961
+ adapter.configured ? "" : ` \xB7 \u7F3A ${adapter.missing.join("\u3001")}`
962
+ ] }, adapter.name)),
963
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("em", { children: [
964
+ "DeepSeek \u53EA\u8D1F\u8D23\u5199\u63D0\u793A\u8BCD\uFF1B\u56FE\u7247\u3001\u89C6\u9891\u3001\u97F3\u4E50\u7531\u4E0A\u9762\u7684\u4F9B\u5E94\u5546 API \u751F\u6210\uFF0CKey \u5728\u542F\u52A8 DSH \u524D\u5199\u5165\u5BBF\u4E3B\u673A\u73AF\u5883\u53D8\u91CF\u3002",
965
+ unconfigured.length === preflight.adapters.length && " \u5F53\u524D\u4E00\u4E2A\u90FD\u6CA1\u914D\u7F6E\uFF0C\u751F\u4EA7\u4EFB\u52A1\u4F1A\u505C\u5728 adapter \u4E4B\u524D\u3002",
966
+ " ",
967
+ "Adapter \u914D\u7F6E",
968
+ preflight.adapterConfig.generated ? "\u5DF2\u81EA\u52A8\u767B\u8BB0" : "\u4F7F\u7528\u81EA\u5B9A\u4E49\u6587\u4EF6",
969
+ preflight.adapterConfig.ok ? "" : "\uFF08\u5199\u5165\u5931\u8D25\uFF09",
970
+ "\uFF1A",
971
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { children: preflight.adapterConfig.path }),
972
+ "\u3002\u8BE6\u89C1 README\u300C\u5A92\u4F53\u751F\u6210 API\u300D\u3002"
973
+ ] })
974
+ ] })
975
+ ] });
976
+ }
909
977
  function handleSectionKey(event, current, onChange) {
910
978
  let index = SECTION_ORDER.indexOf(current), next = event.key === "Home" ? 0 : event.key === "End" ? SECTION_ORDER.length - 1 : event.key === "ArrowRight" ? (index + 1) % SECTION_ORDER.length : event.key === "ArrowLeft" ? (index - 1 + SECTION_ORDER.length) % SECTION_ORDER.length : void 0;
911
979
  next !== void 0 && (event.preventDefault(), onChange(SECTION_ORDER[next]), event.currentTarget.parentElement?.querySelectorAll("[role='tab']")[next]?.focus());
@@ -1006,6 +1074,7 @@ ${item.prompt}`).join(`
1006
1074
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: props.onRefresh, children: "\u5237\u65B0" })
1007
1075
  ] })
1008
1076
  ] }),
1077
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ProductionEnvironment, { sessionId: props.sessionId }),
1009
1078
  notice !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "oh-story-production-notice", role: "status", children: [
1010
1079
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: notice }),
1011
1080
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", "aria-label": "\u5173\u95ED\u63D0\u793A", onClick: () => {
@@ -1083,7 +1152,7 @@ function ShotBoard(props) {
1083
1152
  ] })
1084
1153
  ] }),
1085
1154
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "oh-story-reference-links", children: [
1086
- shot.source !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ReferenceButton, { id: shot.source, production: props.production, onNavigate: props.onNavigate }),
1155
+ shot.sceneIds.length > 0 ? shot.sceneIds.map((sceneId) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ReferenceButton, { id: sceneId, production: props.production, onNavigate: props.onNavigate }, sceneId)) : shot.source !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ReferenceButton, { id: shot.source, production: props.production, onNavigate: props.onNavigate }),
1087
1156
  shot.references.map((id) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ReferenceButton, { id, production: props.production, onNavigate: props.onNavigate }, id))
1088
1157
  ] }),
1089
1158
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "oh-story-card-actions", children: [
@@ -1419,21 +1488,6 @@ function settledProductionIntents(chat) {
1419
1488
 
1420
1489
  // src/client/video-studio.tsx
1421
1490
  var import_react2 = require("react");
1422
-
1423
- // src/client/workbench-ui.ts
1424
- function handleTabKey(event, values, current, select) {
1425
- let index;
1426
- if (event.key === "Home" ? index = 0 : event.key === "End" ? index = values.length - 1 : event.key === "ArrowRight" ? index = (values.indexOf(current) + 1) % values.length : event.key === "ArrowLeft" && (index = (values.indexOf(current) - 1 + values.length) % values.length), index === void 0) return;
1427
- event.preventDefault();
1428
- let value = values[index];
1429
- value !== void 0 && (select(value), event.currentTarget.parentElement?.querySelectorAll("[role='tab']")[index]?.focus());
1430
- }
1431
- function endpoint(path, sessionId, file) {
1432
- let url = new URL(`/oh-story/${path}`, globalThis.location.origin);
1433
- return url.searchParams.set("sessionId", sessionId), file !== void 0 && url.searchParams.set("path", file), url.toString();
1434
- }
1435
-
1436
- // src/client/video-studio.tsx
1437
1491
  var import_jsx_runtime4 = require("react/jsx-runtime");
1438
1492
  function preferredPreview(project) {
1439
1493
  return project.previews.find((item) => item.role === "final") ?? project.previews.find((item) => item.role === "edited") ?? project.previews.find((item) => item.role === "source");
@@ -1644,7 +1698,8 @@ function VideoStudio({
1644
1698
  labelledBy,
1645
1699
  onProject,
1646
1700
  onTab,
1647
- onWorkbench
1701
+ onWorkbench,
1702
+ onCollapse
1648
1703
  }) {
1649
1704
  let project = projects.find((item) => item.id === projectId) ?? projects[0], tabsId = (0, import_react2.useId)();
1650
1705
  (0, import_react2.useEffect)(() => {
@@ -1653,23 +1708,26 @@ function VideoStudio({
1653
1708
  let tabs = ["preview", "artifacts"];
1654
1709
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("main", { id: paneId, className: "oh-video-studio", role: "tabpanel", "aria-labelledby": labelledBy, hidden, children: [
1655
1710
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("header", { className: "oh-video-toolbar", children: [
1656
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "oh-video-mode-tabs", role: "tablist", "aria-label": "\u521B\u4F5C\u5DE5\u4F5C\u53F0", children: workbenches.map((mode) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1657
- "button",
1658
- {
1659
- type: "button",
1660
- role: "tab",
1661
- "aria-selected": mode === "video",
1662
- tabIndex: mode === "video" ? 0 : -1,
1663
- onKeyDown: (event) => {
1664
- handleTabKey(event, workbenches, "video", onWorkbench);
1665
- },
1666
- onClick: () => {
1667
- onWorkbench(mode);
1711
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "oh-workbench-cluster", children: [
1712
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "oh-video-mode-tabs", role: "tablist", "aria-label": "\u521B\u4F5C\u5DE5\u4F5C\u53F0", children: workbenches.map((mode) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1713
+ "button",
1714
+ {
1715
+ type: "button",
1716
+ role: "tab",
1717
+ "aria-selected": mode === "video",
1718
+ tabIndex: mode === "video" ? 0 : -1,
1719
+ onKeyDown: (event) => {
1720
+ handleTabKey(event, workbenches, "video", onWorkbench);
1721
+ },
1722
+ onClick: () => {
1723
+ onWorkbench(mode);
1724
+ },
1725
+ children: workbenchLabel(mode)
1668
1726
  },
1669
- children: workbenchLabel(mode)
1670
- },
1671
- mode
1672
- )) }),
1727
+ mode
1728
+ )) }),
1729
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "oh-workbench-collapse", type: "button", title: "\u6536\u8D77\u521B\u4F5C\u5DE5\u4F5C\u53F0", "aria-label": "\u6536\u8D77\u521B\u4F5C\u5DE5\u4F5C\u53F0", onClick: onCollapse, children: "\xD7" })
1730
+ ] }),
1673
1731
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "oh-video-project", children: [
1674
1732
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u89C6\u9891\u9879\u76EE" }),
1675
1733
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("select", { "aria-label": "\u89C6\u9891\u9879\u76EE", value: project?.id ?? "", disabled: project === void 0, onChange: (event) => {
@@ -1715,14 +1773,94 @@ function VideoStudio({
1715
1773
  ] });
1716
1774
  }
1717
1775
 
1776
+ // src/client/workbench-presence.ts
1777
+ function hasCreativeProject(workspace) {
1778
+ return workspace === void 0 ? !1 : workspace.files.length > 0 || workspace.videos.length > 0 || workspace.games.some((game) => game.source === "workspace");
1779
+ }
1780
+ function resolveWorkbenchOpen(preference, creativeProject) {
1781
+ return preference === void 0 ? creativeProject : preference === "open";
1782
+ }
1783
+ function workbenchPreferenceKey(cwd) {
1784
+ return `oh-story.workbench.${cwd}`;
1785
+ }
1786
+ function readWorkbenchPreference(storage, cwd) {
1787
+ if (storage === void 0 || cwd === void 0) return;
1788
+ let value;
1789
+ try {
1790
+ value = storage.getItem(workbenchPreferenceKey(cwd));
1791
+ } catch {
1792
+ return;
1793
+ }
1794
+ return value === "open" || value === "closed" ? value : void 0;
1795
+ }
1796
+ function writeWorkbenchPreference(storage, cwd, preference) {
1797
+ if (!(storage === void 0 || cwd === void 0))
1798
+ try {
1799
+ storage.setItem(workbenchPreferenceKey(cwd), preference);
1800
+ } catch {
1801
+ }
1802
+ }
1803
+ function workbenchPreferenceStorage() {
1804
+ try {
1805
+ return globalThis.localStorage;
1806
+ } catch {
1807
+ return;
1808
+ }
1809
+ }
1810
+
1718
1811
  // inline-css:/home/runner/work/oh-story-dsh/oh-story-dsh/packages/dsh-plugin/src/client/plugin.css
1719
1812
  var plugin_default = `.oh-story-bridge-marker { display: none; }
1813
+ .oh-story-welcome {
1814
+ box-sizing: border-box;
1815
+ order: -1;
1816
+ flex-shrink: 0;
1817
+ width: min(640px, calc(100% - 32px));
1818
+ margin: 24px auto;
1819
+ padding: 20px 24px;
1820
+ border: 1px solid var(--dsw-alias-border-l2);
1821
+ border-radius: 12px;
1822
+ color: var(--dsw-alias-label-primary);
1823
+ background: var(--dsw-alias-bg-base);
1824
+ font: 14px/1.7 var(--dsw-font-family);
1825
+ overflow-wrap: anywhere;
1826
+ }
1827
+ .oh-story-welcome h2 { margin: 0 0 8px; font-size: 20px; }
1828
+ .oh-story-welcome p { margin: 8px 0 0; }
1829
+ .oh-story-welcome ol { margin: 12px 0; padding-left: 24px; }
1720
1830
  .oh-story-split-surface { display: contents; font-family: var(--dsw-font-family); }
1721
1831
 
1832
+ /* Collapsed, the plugin owns nothing but this control: DSH renders the
1833
+ conversation exactly as it does without the plugin installed. The launcher is
1834
+ fixed to the conversation column's own corner, which the bridge publishes,
1835
+ because the surrounding shell is not ours to place things in. */
1836
+ .oh-story-launcher {
1837
+ box-sizing: border-box;
1838
+ display: flex;
1839
+ position: fixed;
1840
+ z-index: 9;
1841
+ top: calc(var(--oh-story-seam-top, 0px) + 10px);
1842
+ right: calc(var(--oh-story-seam-right, 0px) + 14px);
1843
+ min-height: 28px;
1844
+ align-items: center;
1845
+ gap: 6px;
1846
+ border: 1px solid var(--dsw-alias-border-l2);
1847
+ border-radius: 999px;
1848
+ color: var(--dsw-alias-label-secondary);
1849
+ background: var(--dsw-alias-bg-base);
1850
+ padding: 3px 11px;
1851
+ cursor: pointer;
1852
+ font-family: var(--dsw-font-family);
1853
+ font-size: 12px;
1854
+ line-height: 20px;
1855
+ }
1856
+ .oh-story-launcher b { font-weight: 500; }
1857
+ .oh-story-launcher:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
1858
+ .oh-story-launcher:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary); outline-offset: 2px; }
1859
+
1722
1860
  /* DSH documents every [data-slot] wrapper as a stable addressable styling
1723
1861
  seam. The official Session body and composer stay mounted; this only turns
1724
1862
  their scroll host into a three-column writing surface. */
1725
- [data-conversation-scroll]:has(> [data-slot="conversation.session"] > .oh-story-split-surface) {
1863
+ [data-conversation-scroll]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"]) {
1726
1864
  --os-border: var(--dsw-alias-border-l2);
1727
1865
  --os-muted: var(--dsw-alias-label-secondary);
1728
1866
  display: grid;
@@ -1737,7 +1875,7 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
1737
1875
  }
1738
1876
 
1739
1877
  /* Keep the final Chat flow above the overlapping official Composer. */
1740
- [data-conversation-scroll]:has(> [data-slot="conversation.session"] > .oh-story-split-surface)
1878
+ [data-conversation-scroll]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"])
1741
1879
  > [data-slot="conversation.session"] > :not(.oh-story-split-surface) {
1742
1880
  grid-column: 3;
1743
1881
  grid-row: 1;
@@ -1746,12 +1884,12 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
1746
1884
  border-left: 1px solid var(--os-border);
1747
1885
  }
1748
1886
 
1749
- [data-conversation-scroll]:has(> [data-slot="conversation.session"] > .oh-story-split-surface)
1887
+ [data-conversation-scroll]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"])
1750
1888
  [data-chat-flow] {
1751
1889
  padding-bottom: calc(max(var(--dsh-composer-height, 152px), var(--oh-story-composer-height, 0px)) + 16px);
1752
1890
  }
1753
1891
 
1754
- [data-conversation-scroll]:has(> [data-slot="conversation.session"] > .oh-story-split-surface)
1892
+ [data-conversation-scroll]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"])
1755
1893
  > [data-composer-seat] {
1756
1894
  grid-column: 3;
1757
1895
  grid-row: 1;
@@ -1765,7 +1903,7 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
1765
1903
 
1766
1904
  /* Game mode keeps the official Conversation on the right but merges the two
1767
1905
  creator columns into one playable Studio. */
1768
- [data-conversation-scroll][data-oh-story-workbench="game"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface) {
1906
+ [data-conversation-scroll][data-oh-story-workbench="game"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"]) {
1769
1907
  --game-stage: #0d100e;
1770
1908
  --game-stage-raised: #161a17;
1771
1909
  --game-accent: var(--dsw-alias-state-business-primary);
@@ -1775,9 +1913,9 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
1775
1913
  overflow: hidden;
1776
1914
  }
1777
1915
 
1778
- [data-conversation-scroll][data-oh-story-workbench="game"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface)
1916
+ [data-conversation-scroll][data-oh-story-workbench="game"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"])
1779
1917
  > [data-slot="conversation.session"] > :not(.oh-story-split-surface),
1780
- [data-conversation-scroll][data-oh-story-workbench="game"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface)
1918
+ [data-conversation-scroll][data-oh-story-workbench="game"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"])
1781
1919
  > [data-composer-seat] { grid-column: 2; }
1782
1920
 
1783
1921
  /* DSH 0.1.2 lays two 40px column-resize strips (z-index 8) over the whole
@@ -2053,6 +2191,27 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
2053
2191
  .oh-story-role button:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary); outline-offset: -2px; }
2054
2192
  .oh-story-save:disabled { opacity: .48; cursor: default; }
2055
2193
 
2194
+ .oh-story-brand-actions { display: flex; flex: none; align-items: center; gap: 2px; }
2195
+
2196
+ .oh-workbench-cluster { display: flex; min-width: 0; align-items: center; gap: 6px; }
2197
+ .oh-workbench-collapse {
2198
+ display: grid;
2199
+ width: 28px;
2200
+ height: 28px;
2201
+ flex: none;
2202
+ place-items: center;
2203
+ border: 0;
2204
+ border-radius: 5px;
2205
+ color: var(--os-muted);
2206
+ background: transparent;
2207
+ padding: 0;
2208
+ cursor: pointer;
2209
+ font: inherit;
2210
+ font-size: 16px;
2211
+ }
2212
+ .oh-workbench-collapse:hover { color: var(--dsw-alias-label-primary); background: var(--dsw-alias-interactive-bg-hover); }
2213
+ .oh-workbench-collapse:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary); outline-offset: -2px; }
2214
+
2056
2215
  .oh-story-mode-tabs { display: flex; margin: 0 4px 8px; padding: 2px; border: 1px solid var(--os-border); border-radius: 7px; background: var(--dsw-alias-bg-layer-1); }
2057
2216
 
2058
2217
  .oh-story-mode-tabs button,
@@ -2313,6 +2472,16 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
2313
2472
  .oh-story-production-summary { color: var(--os-muted); white-space: nowrap; }
2314
2473
  .oh-story-production-notice { justify-content: space-between; margin: 8px 12px 0; padding: 7px 9px; border: 1px solid var(--os-border); border-radius: 7px; color: var(--dsw-alias-label-secondary); background: var(--dsw-alias-bg-layer-1); }
2315
2474
  .oh-story-production-notice button { border: 0; background: transparent; }
2475
+ .oh-story-production-environment { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 5px 10px; margin: 8px 12px 0; padding: 7px 9px; border: 1px solid var(--os-border); border-radius: 7px; color: var(--os-muted); background: var(--dsw-alias-bg-layer-1); font-size: 10px; }
2476
+ /* Environment names such as MINIMAX_VIDEO_RESOLUTIONS and the temp-dir path are longer than the 500 px editor column; let them break rather than widen the view. */
2477
+ .oh-story-production-environment > * { min-width: 0; max-width: 100%; overflow-wrap: anywhere; }
2478
+ .oh-story-production-environment strong { color: var(--dsw-alias-label-secondary); font-size: 11px; }
2479
+ .oh-story-production-environment span::before { content: "\u25CF"; margin-right: 4px; color: var(--video-danger, #d9534f); }
2480
+ .oh-story-production-environment span[data-ready]::before { color: var(--dsw-alias-state-success-primary, #45b96f); }
2481
+ .oh-story-production-environment span[data-pending]::before { color: var(--os-muted); }
2482
+ .oh-story-production-environment em { flex-basis: 100%; font-style: normal; line-height: 1.5; }
2483
+ .oh-story-production-environment code { font-size: 10px; overflow-wrap: anywhere; }
2484
+ .oh-story-production-environment button { min-height: 24px; border: 1px solid var(--os-border); border-radius: 6px; padding: 0 8px; color: inherit; background: var(--dsw-alias-bg-base); cursor: pointer; font: inherit; }
2316
2485
  .oh-story-production-diagnostics { margin: 8px 12px 0; padding: 7px 9px; border: 1px solid var(--dsw-alias-state-warn-label); border-radius: 7px; background: var(--dsw-specific-tip); }
2317
2486
  .oh-story-production-diagnostics summary { color: var(--dsw-alias-state-warn-label); cursor: pointer; }
2318
2487
  .oh-story-production-diagnostics ul { display: grid; gap: 4px; margin: 8px 0 0; padding: 0; list-style: none; }
@@ -2436,11 +2605,11 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
2436
2605
  .oh-story-canvas article span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2437
2606
  .oh-story-canvas article span { color: var(--os-muted); font-size: 10px; }
2438
2607
 
2439
- [data-conversation-scroll][data-oh-story-layout="medium"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface) {
2608
+ [data-conversation-scroll][data-oh-story-layout="medium"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"]) {
2440
2609
  grid-template-columns: clamp(104px, 18%, 184px) minmax(200px, 1fr) clamp(300px, 45%, 408px);
2441
2610
  }
2442
2611
 
2443
- [data-conversation-scroll][data-oh-story-layout="compact"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface) {
2612
+ [data-conversation-scroll][data-oh-story-layout="compact"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"]) {
2444
2613
  grid-template-columns: clamp(80px, 16%, 104px) minmax(0, 1fr) clamp(228px, 48%, 300px);
2445
2614
  }
2446
2615
 
@@ -2483,12 +2652,12 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
2483
2652
  [data-oh-story-layout="compact"] .oh-story-jsonl summary > code,
2484
2653
  [data-oh-story-layout="compact"] .oh-story-jsonl summary > em { display: none; }
2485
2654
 
2486
- [data-conversation-scroll][data-oh-story-workbench="game"][data-oh-story-layout="medium"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface) {
2655
+ [data-conversation-scroll][data-oh-story-workbench="game"][data-oh-story-layout="medium"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"]) {
2487
2656
  grid-template-columns: minmax(0, 1fr) clamp(360px, 42%, 440px);
2488
2657
  }
2489
2658
  [data-oh-story-workbench="game"][data-oh-story-layout="medium"] .oh-game-toolbar { grid-template-columns: auto minmax(140px, 1fr) auto; }
2490
2659
 
2491
- [data-conversation-scroll][data-oh-story-workbench="game"][data-oh-story-layout="compact"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface) {
2660
+ [data-conversation-scroll][data-oh-story-workbench="game"][data-oh-story-layout="compact"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"]) {
2492
2661
  display: grid;
2493
2662
  grid-template-columns: minmax(0, 1fr);
2494
2663
  overflow: hidden;
@@ -2523,7 +2692,7 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
2523
2692
  gap: 5px;
2524
2693
  padding: 4px 8px;
2525
2694
  }
2526
- [data-conversation-scroll][data-oh-story-workbench="game"][data-oh-story-layout="compact"] .oh-game-mode-tabs { grid-column: 1; grid-row: 1; width: fit-content; }
2695
+ [data-conversation-scroll][data-oh-story-workbench="game"][data-oh-story-layout="compact"] .oh-game-toolbar > .oh-workbench-cluster { grid-column: 1; grid-row: 1; width: fit-content; }
2527
2696
  [data-conversation-scroll][data-oh-story-workbench="game"][data-oh-story-layout="compact"] .oh-game-project > span { display: none; }
2528
2697
  [data-conversation-scroll][data-oh-story-workbench="game"][data-oh-story-layout="compact"] .oh-game-project { grid-column: 1 / -1; grid-row: 2; }
2529
2698
  [data-conversation-scroll][data-oh-story-workbench="game"][data-oh-story-layout="compact"] .oh-game-project select { max-width: none; width: 100%; min-height: 44px; }
@@ -2549,7 +2718,7 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
2549
2718
  align-content: center;
2550
2719
  padding: 4px 8px;
2551
2720
  }
2552
- [data-conversation-scroll][data-oh-story-workbench="game"][data-oh-story-layout="compact"] .oh-game-studio[data-oh-game-narrow] .oh-game-mode-tabs {
2721
+ [data-conversation-scroll][data-oh-story-workbench="game"][data-oh-story-layout="compact"] .oh-game-studio[data-oh-game-narrow] .oh-game-toolbar > .oh-workbench-cluster {
2553
2722
  grid-column: 1;
2554
2723
  grid-row: 2;
2555
2724
  justify-self: start;
@@ -2607,7 +2776,7 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
2607
2776
 
2608
2777
  /* Video mode intentionally stays a preview surface: one player, a read-only artifact view,
2609
2778
  and the official DSH Conversation. It shares no nonlinear-editor or timeline runtime. */
2610
- [data-conversation-scroll][data-oh-story-workbench="video"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface) {
2779
+ [data-conversation-scroll][data-oh-story-workbench="video"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"]) {
2611
2780
  --video-stage: #0b0d10;
2612
2781
  --video-stage-raised: #15181d;
2613
2782
  --video-accent: var(--dsw-alias-state-business-primary);
@@ -2616,9 +2785,9 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
2616
2785
  grid-template-columns: minmax(0, 1fr) clamp(400px, 34%, 520px);
2617
2786
  overflow: hidden;
2618
2787
  }
2619
- [data-conversation-scroll][data-oh-story-workbench="video"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface)
2788
+ [data-conversation-scroll][data-oh-story-workbench="video"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"])
2620
2789
  > [data-slot="conversation.session"] > :not(.oh-story-split-surface),
2621
- [data-conversation-scroll][data-oh-story-workbench="video"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface)
2790
+ [data-conversation-scroll][data-oh-story-workbench="video"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"])
2622
2791
  > [data-composer-seat] { grid-column: 2; }
2623
2792
 
2624
2793
  .oh-video-studio {
@@ -2762,10 +2931,10 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
2762
2931
  .oh-video-artifact-content { min-width: 0; min-height: 0; flex: 1; overflow: auto; }
2763
2932
  .oh-video-artifact-content pre { min-width: 0; min-height: 100%; box-sizing: border-box; margin: 0; padding: 20px; color: inherit; background: var(--dsw-alias-bg-base); white-space: pre-wrap; overflow-wrap: anywhere; font: 12px/1.65 var(--dsw-font-family-mono, ui-monospace, monospace); }
2764
2933
 
2765
- [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="medium"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface) { grid-template-columns: minmax(0, 1fr) clamp(360px, 42%, 440px); }
2934
+ [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="medium"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"]) { grid-template-columns: minmax(0, 1fr) clamp(360px, 42%, 440px); }
2766
2935
  [data-oh-story-workbench="video"][data-oh-story-layout="medium"] .oh-video-project select { width: 100%; }
2767
2936
 
2768
- [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="compact"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface) { display: grid; grid-template-columns: minmax(0, 1fr); overflow: hidden; }
2937
+ [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="compact"]:has(> [data-slot="conversation.session"] > .oh-story-split-surface[data-open="true"]) { display: grid; grid-template-columns: minmax(0, 1fr); overflow: hidden; }
2769
2938
  [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="compact"]
2770
2939
  > [data-slot="conversation.session"] > :not(.oh-story-split-surface),
2771
2940
  [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="compact"]
@@ -2778,7 +2947,7 @@ var plugin_default = `.oh-story-bridge-marker { display: none; }
2778
2947
  [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="compact"][data-oh-studio-pane="chat"] .oh-video-studio { display: none; }
2779
2948
  [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="compact"] .oh-game-mobile-switcher { display: flex; position: absolute; top: 8px; right: 8px; z-index: 20; border-color: var(--dsw-alias-border-l1); box-shadow: 0 4px 14px rgb(0 0 0 / 12%); }
2780
2949
  [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="compact"] .oh-video-toolbar { min-height: 142px; grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: 44px 44px 38px; align-content: center; gap: 2px 6px; padding: 4px 8px; }
2781
- [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="compact"] .oh-video-mode-tabs { grid-column: 1 / -1; grid-row: 1; width: fit-content; }
2950
+ [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="compact"] .oh-video-toolbar > .oh-workbench-cluster { grid-column: 1 / -1; grid-row: 1; width: fit-content; }
2782
2951
  [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="compact"] .oh-video-tabs { grid-column: 2; grid-row: 2; justify-self: end; }
2783
2952
  [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="compact"] .oh-video-project { grid-column: 1; grid-row: 2; }
2784
2953
  [data-conversation-scroll][data-oh-story-workbench="video"][data-oh-story-layout="compact"] .oh-video-project > span { display: none; }
@@ -2801,6 +2970,7 @@ function createWorkbenchStore() {
2801
2970
  return (0, import_dsh_client_store.defineStore)({
2802
2971
  init: () => ({
2803
2972
  buffers: {},
2973
+ workbenchPreference: void 0,
2804
2974
  editorMode: "preview",
2805
2975
  expanded: {},
2806
2976
  selected: void 0,
@@ -2825,6 +2995,9 @@ function createWorkbenchStore() {
2825
2995
  setBuffers: (draft, update) => {
2826
2996
  draft.buffers = applyUpdate(draft.buffers, update);
2827
2997
  },
2998
+ setWorkbenchPreference: (draft, update) => {
2999
+ draft.workbenchPreference = applyUpdate(draft.workbenchPreference, update);
3000
+ },
2828
3001
  setEditorMode: (draft, update) => {
2829
3002
  draft.editorMode = applyUpdate(draft.editorMode, update);
2830
3003
  },
@@ -3126,6 +3299,7 @@ function GameStudio({
3126
3299
  paneId,
3127
3300
  labelledBy,
3128
3301
  onWorkbench,
3302
+ onCollapse,
3129
3303
  onSelect
3130
3304
  }) {
3131
3305
  let project = workspace.games.find((value) => value.id === gameProjectId) ?? workspace.games[0], studioRef = (0, import_react3.useRef)(null), tabsId = (0, import_react3.useId)();
@@ -3146,23 +3320,26 @@ function GameStudio({
3146
3320
  let tabs = ["preview", "design"];
3147
3321
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("main", { ref: studioRef, id: paneId, className: "oh-game-studio", "data-source": project.source, role: "tabpanel", "aria-labelledby": labelledBy, hidden, children: [
3148
3322
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("header", { className: "oh-game-toolbar", children: [
3149
- workbenches.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "oh-game-mode-tabs", role: "tablist", "aria-label": "\u521B\u4F5C\u5DE5\u4F5C\u53F0", children: workbenches.map((mode) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3150
- "button",
3151
- {
3152
- type: "button",
3153
- role: "tab",
3154
- "aria-selected": mode === "game",
3155
- tabIndex: mode === "game" ? 0 : -1,
3156
- onKeyDown: (event) => {
3157
- handleTabKey(event, workbenches, "game", onWorkbench);
3158
- },
3159
- onClick: () => {
3160
- onWorkbench(mode);
3323
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "oh-workbench-cluster", children: [
3324
+ workbenches.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "oh-game-mode-tabs", role: "tablist", "aria-label": "\u521B\u4F5C\u5DE5\u4F5C\u53F0", children: workbenches.map((mode) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3325
+ "button",
3326
+ {
3327
+ type: "button",
3328
+ role: "tab",
3329
+ "aria-selected": mode === "game",
3330
+ tabIndex: mode === "game" ? 0 : -1,
3331
+ onKeyDown: (event) => {
3332
+ handleTabKey(event, workbenches, "game", onWorkbench);
3333
+ },
3334
+ onClick: () => {
3335
+ onWorkbench(mode);
3336
+ },
3337
+ children: workbenchLabel(mode)
3161
3338
  },
3162
- children: workbenchLabel(mode)
3163
- },
3164
- mode
3165
- )) }),
3339
+ mode
3340
+ )) }),
3341
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "oh-workbench-collapse", type: "button", title: "\u6536\u8D77\u521B\u4F5C\u5DE5\u4F5C\u53F0", "aria-label": "\u6536\u8D77\u521B\u4F5C\u5DE5\u4F5C\u53F0", onClick: onCollapse, children: "\xD7" })
3342
+ ] }),
3166
3343
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "oh-game-project", title: "\u5207\u6362\u9879\u76EE\u5C06\u91CD\u65B0\u8F7D\u5165\u8BD5\u73A9", children: [
3167
3344
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "\u6E38\u620F\u9879\u76EE" }),
3168
3345
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("select", { "aria-label": "\u6E38\u620F\u9879\u76EE\uFF1B\u5207\u6362\u5C06\u91CD\u65B0\u8F7D\u5165\u8BD5\u73A9", value: project.id, onChange: (event) => {
@@ -3209,16 +3386,24 @@ function CreativeWorkbench({
3209
3386
  sendProductionPrompt,
3210
3387
  cancelProduction,
3211
3388
  removeQueuedProduction,
3389
+ workspace,
3390
+ error,
3391
+ workspaceLoading,
3392
+ reload,
3393
+ open,
3394
+ creativeProject,
3212
3395
  useStore,
3213
3396
  actions
3214
3397
  }) {
3215
- let { workspace, error, loading: workspaceLoading, reload } = useWorkspace(sessionId), activities = (0, import_react3.useMemo)(
3398
+ let activities = (0, import_react3.useMemo)(
3216
3399
  () => fileMutations(runningCalls, partial),
3217
3400
  [partial, runningCalls]
3218
3401
  ), normalizedActivities = (0, import_react3.useMemo)(() => activities.flatMap((activity2) => {
3219
3402
  let path = creativeRelativePath(activity2.path, workspace?.cwd);
3220
3403
  return path === void 0 ? [] : [{ activity: activity2, path }];
3221
- }), [activities, workspace?.cwd]), primaryActivity = normalizedActivities.at(-1), activityPaths = (0, import_react3.useMemo)(() => new Set(normalizedActivities.map((value) => value.path)), [normalizedActivities]), activity = primaryActivity?.activity, activityPath = primaryActivity?.path, workbench = useStore((memory) => memory.workbench), setWorkbench = actions.setWorkbench, gameTab = useStore((memory) => memory.gameTab), setGameTab = actions.setGameTab, gameProjectId = useStore((memory) => memory.gameProjectId), setGameProjectId = actions.setGameProjectId, gamePane = useStore((memory) => memory.gamePane), setGamePane = actions.setGamePane, videoTab = useStore((memory) => memory.videoTab), setVideoTab = actions.setVideoTab, videoProjectId = useStore((memory) => memory.videoProjectId), setVideoProjectId = actions.setVideoProjectId, videoPane = useStore((memory) => memory.videoPane), setVideoPane = actions.setVideoPane, selected = useStore((memory) => memory.selected), setSelected = actions.setSelected, buffers = useStore((memory) => memory.buffers), setBuffers = actions.setBuffers, buffersRef = (0, import_react3.useRef)({}), expanded = useStore((memory) => memory.expanded), setExpanded = actions.setExpanded, productionSection = useStore((memory) => memory.productionSection), setProductionSection = actions.setProductionSection, productionSelectedIds = useStore((memory) => memory.productionSelectedIds), productionJobsByEpisode = useStore((memory) => memory.productionJobs), productionSelectionsByEpisode = useStore((memory) => memory.productionSelections), productionReferencesByEpisode = useStore((memory) => memory.productionReferences), productionSequenceByEpisode = useStore((memory) => memory.productionSequence), productionCanvasByEpisode = useStore((memory) => memory.productionCanvas), productionZoomByEpisode = useStore((memory) => memory.productionZoom), productionIntentCalls = useStore((memory) => memory.productionIntentCalls), surfaceRef = (0, import_react3.useRef)(null), compactTabsId = (0, import_react3.useId)(), compactStudioId = `${compactTabsId}-studio-panel`, compactVideoStudioId = `${compactTabsId}-video-studio-panel`, compactChatId = `${compactTabsId}-chat-panel`, navRef = (0, import_react3.useRef)(null), activityBases = (0, import_react3.useRef)(/* @__PURE__ */ new Map()), previousSignals = (0, import_react3.useRef)(/* @__PURE__ */ new Set()), previousSettledMutation = (0, import_react3.useRef)(settledMutation), saveLocks = (0, import_react3.useRef)(/* @__PURE__ */ new Set()), buffer = selected === void 0 ? void 0 : buffers[selected], selectedFile = workspace?.files.find((file) => file.path === selected), selectedMedia = selectedFile?.kind === "media", dirty = buffer?.source === "human" && buffer.content !== buffer.saved, saving = buffer?.saving === !0, fileError = buffer?.error, conflict = buffer?.conflict, selectedLower = selected?.toLocaleLowerCase(), markdown = selectedLower?.endsWith(".md") === !0, jsonl = selectedLower?.endsWith(".jsonl") === !0, structured = jsonl || selectedLower?.endsWith(".json") === !0, previewable = markdown || jsonl, episodeDirectory = episodeDirectoryForPath(selected), productionAvailable = selected !== void 0 && isCreatorDocumentPath(selected) && episodeDirectory !== void 0, editorModes = productionAvailable ? EDITOR_MODES : EDITOR_MODES.filter((mode) => mode !== "production"), episodeDocumentPaths = (0, import_react3.useMemo)(
3404
+ }), [activities, workspace?.cwd]), primaryActivity = normalizedActivities.at(-1), activityPaths = (0, import_react3.useMemo)(() => new Set(normalizedActivities.map((value) => value.path)), [normalizedActivities]), activity = primaryActivity?.activity, activityPath = primaryActivity?.path, workbench = useStore((memory) => memory.workbench), setWorkbench = actions.setWorkbench, gameTab = useStore((memory) => memory.gameTab), setGameTab = actions.setGameTab, gameProjectId = useStore((memory) => memory.gameProjectId), setGameProjectId = actions.setGameProjectId, gamePane = useStore((memory) => memory.gamePane), setGamePane = actions.setGamePane, videoTab = useStore((memory) => memory.videoTab), setVideoTab = actions.setVideoTab, videoProjectId = useStore((memory) => memory.videoProjectId), setVideoProjectId = actions.setVideoProjectId, videoPane = useStore((memory) => memory.videoPane), setVideoPane = actions.setVideoPane, selected = useStore((memory) => memory.selected), setSelected = actions.setSelected, buffers = useStore((memory) => memory.buffers), setBuffers = actions.setBuffers, buffersRef = (0, import_react3.useRef)({}), expanded = useStore((memory) => memory.expanded), setExpanded = actions.setExpanded, productionSection = useStore((memory) => memory.productionSection), setProductionSection = actions.setProductionSection, productionSelectedIds = useStore((memory) => memory.productionSelectedIds), productionJobsByEpisode = useStore((memory) => memory.productionJobs), productionSelectionsByEpisode = useStore((memory) => memory.productionSelections), productionReferencesByEpisode = useStore((memory) => memory.productionReferences), productionSequenceByEpisode = useStore((memory) => memory.productionSequence), productionCanvasByEpisode = useStore((memory) => memory.productionCanvas), productionZoomByEpisode = useStore((memory) => memory.productionZoom), productionIntentCalls = useStore((memory) => memory.productionIntentCalls), surfaceRef = (0, import_react3.useRef)(null), setWorkbenchPreference = actions.setWorkbenchPreference, applyWorkbenchPreference = (0, import_react3.useCallback)((preference) => {
3405
+ setWorkbenchPreference(preference), writeWorkbenchPreference(workbenchPreferenceStorage(), workspace?.cwd, preference);
3406
+ }, [setWorkbenchPreference, workspace?.cwd]), compactTabsId = (0, import_react3.useId)(), compactStudioId = `${compactTabsId}-studio-panel`, compactVideoStudioId = `${compactTabsId}-video-studio-panel`, compactChatId = `${compactTabsId}-chat-panel`, navRef = (0, import_react3.useRef)(null), activityBases = (0, import_react3.useRef)(/* @__PURE__ */ new Map()), previousSignals = (0, import_react3.useRef)(/* @__PURE__ */ new Set()), previousSettledMutation = (0, import_react3.useRef)(settledMutation), saveLocks = (0, import_react3.useRef)(/* @__PURE__ */ new Set()), buffer = selected === void 0 ? void 0 : buffers[selected], selectedFile = workspace?.files.find((file) => file.path === selected), selectedMedia = selectedFile?.kind === "media", dirty = buffer?.source === "human" && buffer.content !== buffer.saved, saving = buffer?.saving === !0, fileError = buffer?.error, conflict = buffer?.conflict, selectedLower = selected?.toLocaleLowerCase(), markdown = selectedLower?.endsWith(".md") === !0, jsonl = selectedLower?.endsWith(".jsonl") === !0, structured = jsonl || selectedLower?.endsWith(".json") === !0, previewable = markdown || jsonl, episodeDirectory = episodeDirectoryForPath(selected), productionAvailable = selected !== void 0 && isCreatorDocumentPath(selected) && episodeDirectory !== void 0, editorModes = productionAvailable ? EDITOR_MODES : EDITOR_MODES.filter((mode) => mode !== "production"), episodeDocumentPaths = (0, import_react3.useMemo)(
3222
3407
  () => episodeDirectory === void 0 ? [] : creatorDocumentPaths(workspace?.files.filter((file) => file.kind === "text") ?? [], episodeDirectory),
3223
3408
  [episodeDirectory, workspace?.files]
3224
3409
  ), episodeDocuments = (0, import_react3.useMemo)(() => Object.fromEntries(episodeDocumentPaths.flatMap((path) => {
@@ -3506,7 +3691,7 @@ function CreativeWorkbench({
3506
3691
  }
3507
3692
  }
3508
3693
  }, [selected]), (0, import_react3.useEffect)(() => {
3509
- if (normalizedActivities.length > 0 || workspace === void 0) return;
3694
+ if (!open || normalizedActivities.length > 0 || workspace === void 0) return;
3510
3695
  let sessionSurface = surfaceRef.current?.parentElement;
3511
3696
  if (sessionSurface == null) return;
3512
3697
  let knownPaths = new Set(workspace.files.map((file) => file.path)), followOfficialFileLink = (event) => {
@@ -3526,7 +3711,7 @@ function CreativeWorkbench({
3526
3711
  return sessionSurface.addEventListener("click", followOfficialFileLink, !0), () => {
3527
3712
  sessionSurface.removeEventListener("click", followOfficialFileLink, !0);
3528
3713
  };
3529
- }, [normalizedActivities.length, revealPath, workspace]), (0, import_react3.useEffect)(() => {
3714
+ }, [normalizedActivities.length, open, revealPath, workspace]), (0, import_react3.useEffect)(() => {
3530
3715
  if (workbench !== "game" && workbench !== "video") return;
3531
3716
  let surface = surfaceRef.current, sessionSurface = surface?.parentElement, chat = Array.from(sessionSurface?.children ?? []).find((child) => child !== surface && child instanceof HTMLElement);
3532
3717
  if (!(chat instanceof HTMLElement)) return;
@@ -3610,13 +3795,14 @@ function CreativeWorkbench({
3610
3795
  }
3611
3796
  }, [reload, sessionId]);
3612
3797
  (0, import_react3.useEffect)(() => {
3798
+ if (!open) return;
3613
3799
  let saveShortcut = (event) => {
3614
3800
  !(event.metaKey || event.ctrlKey) || event.key.toLocaleLowerCase() !== "s" || (event.preventDefault(), selected !== void 0 && savePath(selected));
3615
3801
  };
3616
3802
  return globalThis.addEventListener("keydown", saveShortcut), () => {
3617
3803
  globalThis.removeEventListener("keydown", saveShortcut);
3618
3804
  };
3619
- }, [savePath, selected]);
3805
+ }, [open, savePath, selected]);
3620
3806
  let groups = (0, import_react3.useMemo)(() => {
3621
3807
  let value = /* @__PURE__ */ new Map(), all = [...workspace?.files ?? []].filter((file) => workbenchModeForPath(file.path) === workbench);
3622
3808
  activityPath !== void 0 && !all.some((file) => file.path === activityPath) && all.push({ path: activityPath, bytes: 0, version: "", kind: "text" }), all.sort((left, right) => left.path.localeCompare(right.path, "zh-Hans-CN"));
@@ -3641,8 +3827,8 @@ function CreativeWorkbench({
3641
3827
  }, navigateProductionTarget = (target) => {
3642
3828
  let before = (buffersRef.current[target.path]?.content ?? "").slice(0, target.offset), approximateScrollTop = Math.max(0, before.split(/\r?\n/u).length * 28 - 96);
3643
3829
  editorPositions.current.set(target.path, { scrollTop: approximateScrollTop, selectionStart: target.offset, selectionEnd: target.offset }), modeSelection.current = target.path, revealPath(target.path), setEditorMode("source");
3644
- }, selectedLabel = selected ?? `\u5728\u5F53\u524D DSH workspace \u4E2D\u9009\u62E9${workbenchLabel(workbench)}\u6587\u4EF6`, selectedBasename = selected?.split("/").at(-1) ?? selectedLabel, selectedGroup = selected === void 0 ? void 0 : groupForPath(selected), toggleGroup = (key, open) => {
3645
- setExpanded((current) => ({ ...current, [key]: open }));
3830
+ }, selectedLabel = selected ?? `\u5728\u5F53\u524D DSH workspace \u4E2D\u9009\u62E9${workbenchLabel(workbench)}\u6587\u4EF6`, selectedBasename = selected?.split("/").at(-1) ?? selectedLabel, selectedGroup = selected === void 0 ? void 0 : groupForPath(selected), toggleGroup = (key, open2) => {
3831
+ setExpanded((current) => ({ ...current, [key]: open2 }));
3646
3832
  }, resolveConflict = (keepLocal) => {
3647
3833
  if (selected === void 0 || conflict?.theirs === void 0 || conflict.theirsVersion === void 0) return;
3648
3834
  let theirs = conflict.theirs, theirsVersion = conflict.theirsVersion;
@@ -3654,7 +3840,7 @@ function CreativeWorkbench({
3654
3840
  };
3655
3841
  });
3656
3842
  };
3657
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: surfaceRef, className: "oh-story-split-surface", "data-workbench": workbench, children: [
3843
+ return open ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: surfaceRef, className: "oh-story-split-surface", "data-open": "true", "data-workbench": workbench, children: [
3658
3844
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: plugin_default }),
3659
3845
  (workbench === "game" || workbench === "video") && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "oh-game-mobile-switcher", role: "tablist", "aria-label": workbench === "game" ? "\u7A84\u5C4F\u6E38\u620F\u5DE5\u4F5C\u53F0" : "\u7A84\u5C4F\u89C6\u9891\u5DE5\u4F5C\u53F0", children: ["studio", "chat"].map((pane) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3660
3846
  "button",
@@ -3692,6 +3878,9 @@ function CreativeWorkbench({
3692
3878
  paneId: compactStudioId,
3693
3879
  labelledBy: `${compactTabsId}-studio-tab`,
3694
3880
  onWorkbench: selectWorkbench,
3881
+ onCollapse: () => {
3882
+ applyWorkbenchPreference("closed");
3883
+ },
3695
3884
  onSelect: revealPath
3696
3885
  }
3697
3886
  ),
@@ -3710,7 +3899,10 @@ function CreativeWorkbench({
3710
3899
  labelledBy: `${compactTabsId}-studio-tab`,
3711
3900
  onProject: setVideoProjectId,
3712
3901
  onTab: setVideoTab,
3713
- onWorkbench: selectWorkbench
3902
+ onWorkbench: selectWorkbench,
3903
+ onCollapse: () => {
3904
+ applyWorkbenchPreference("closed");
3905
+ }
3714
3906
  }
3715
3907
  ),
3716
3908
  workbench !== "game" && workbench !== "video" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
@@ -3723,7 +3915,12 @@ function CreativeWorkbench({
3723
3915
  ] }),
3724
3916
  workspaceKind !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "oh-story-kind", children: workspaceKind === "story" ? "\u5C0F\u8BF4" : "\u77ED\u5267" })
3725
3917
  ] }),
3726
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: reload, title: "\u5237\u65B0", "aria-label": "\u5237\u65B0\u9879\u76EE\u6587\u4EF6", children: "\u21BB" })
3918
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "oh-story-brand-actions", children: [
3919
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: reload, title: "\u5237\u65B0", "aria-label": "\u5237\u65B0\u9879\u76EE\u6587\u4EF6", children: "\u21BB" }),
3920
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: () => {
3921
+ applyWorkbenchPreference("closed");
3922
+ }, title: "\u6536\u8D77\u521B\u4F5C\u5DE5\u4F5C\u53F0", "aria-label": "\u6536\u8D77\u521B\u4F5C\u5DE5\u4F5C\u53F0", children: "\xD7" })
3923
+ ] })
3727
3924
  ] }),
3728
3925
  workspace !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "oh-story-mode-tabs", role: "tablist", "aria-label": "\u521B\u4F5C\u5DE5\u4F5C\u53F0", children: WORKBENCH_MODES.map((mode) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3729
3926
  "button",
@@ -3836,6 +4033,7 @@ function CreativeWorkbench({
3836
4033
  ] }) : editorMode === "production" && productionAvailable && episodeProduction !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3837
4034
  DramaProductionView,
3838
4035
  {
4036
+ sessionId,
3839
4037
  production: episodeProduction,
3840
4038
  sessionRunning,
3841
4039
  queue: productionQueue,
@@ -3895,10 +4093,21 @@ function CreativeWorkbench({
3895
4093
  )
3896
4094
  ] })
3897
4095
  ] })
4096
+ ] }) : !creativeProject && error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: surfaceRef, className: "oh-story-split-surface", "data-open": "false", children: [
4097
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: plugin_default }),
4098
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("button", { className: "oh-story-launcher", type: "button", title: error ?? "\u6253\u5F00\u521B\u4F5C\u5DE5\u4F5C\u53F0", "aria-label": "\u6253\u5F00\u521B\u4F5C\u5DE5\u4F5C\u53F0", onClick: () => {
4099
+ applyWorkbenchPreference("open");
4100
+ }, children: [
4101
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-hidden": !0, children: "\u2726" }),
4102
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("b", { children: "\u521B\u4F5C\u5DE5\u4F5C\u53F0" })
4103
+ ] })
3898
4104
  ] });
3899
4105
  }
3900
4106
  function CreativeSplitBridge({ sessionId, useSession, useChat, useStore, actions, sendProductionPrompt, cancelProduction, removeQueuedProduction }) {
3901
- let marker = (0, import_react3.useRef)(null), [target, setTarget] = (0, import_react3.useState)(), runningCalls = useChat((snapshot) => snapshot.legacy.runningCalls), partial = useChat((snapshot) => streamingAssistant(snapshot.timeline)), settledMutation = useChat((snapshot) => latestSettledMutation(snapshot)), workbench = useStore((memory) => memory.workbench), gamePane = useStore((memory) => memory.gamePane), videoPane = useStore((memory) => memory.videoPane), sessionRunning = useSession((snapshot) => snapshot.running), productionQueue = useSession((snapshot) => snapshot.queue.map((item) => ({ id: item.id, preview: item.preview }))), chat = useChat((snapshot) => snapshot), productionIntents = (0, import_react3.useMemo)(() => settledProductionIntents(chat), [chat]);
4107
+ let marker = (0, import_react3.useRef)(null), [target, setTarget] = (0, import_react3.useState)(), runningCalls = useChat((snapshot) => snapshot.legacy.runningCalls), partial = useChat((snapshot) => streamingAssistant(snapshot.timeline)), settledMutation = useChat((snapshot) => latestSettledMutation(snapshot)), workbench = useStore((memory) => memory.workbench), gamePane = useStore((memory) => memory.gamePane), videoPane = useStore((memory) => memory.videoPane), sessionRunning = useSession((snapshot) => snapshot.running), productionQueue = useSession((snapshot) => snapshot.queue.map((item) => ({ id: item.id, preview: item.preview }))), chat = useChat((snapshot) => snapshot), productionIntents = (0, import_react3.useMemo)(() => settledProductionIntents(chat), [chat]), { workspace, error, loading: workspaceLoading, reload } = useWorkspace(sessionId), chosenPreference = useStore((memory) => memory.workbenchPreference), creativeProject = hasCreativeProject(workspace), storedPreference = (0, import_react3.useMemo)(
4108
+ () => readWorkbenchPreference(workbenchPreferenceStorage(), workspace?.cwd),
4109
+ [workspace?.cwd]
4110
+ ), open = resolveWorkbenchOpen(chosenPreference ?? storedPreference, creativeProject);
3902
4111
  return (0, import_react3.useLayoutEffect)(() => {
3903
4112
  let document = marker.current?.ownerDocument;
3904
4113
  if (document === void 0) return;
@@ -3914,8 +4123,21 @@ function CreativeSplitBridge({ sessionId, useSession, useChat, useStore, actions
3914
4123
  }, [sessionId]), (0, import_react3.useLayoutEffect)(() => {
3915
4124
  let scroller = target?.parentElement;
3916
4125
  if (scroller == null) return;
4126
+ if (!open) {
4127
+ let publishSeam = () => {
4128
+ let box = scroller.getBoundingClientRect();
4129
+ scroller.style.setProperty("--oh-story-seam-top", `${String(box.top)}px`), scroller.style.setProperty("--oh-story-seam-right", `${String(scroller.ownerDocument.documentElement.clientWidth - box.right)}px`);
4130
+ };
4131
+ publishSeam();
4132
+ let seams = new ResizeObserver(publishSeam);
4133
+ seams.observe(scroller);
4134
+ let view = scroller.ownerDocument.defaultView;
4135
+ return view?.addEventListener("resize", publishSeam), () => {
4136
+ seams.disconnect(), view?.removeEventListener("resize", publishSeam), scroller.style.removeProperty("--oh-story-seam-top"), scroller.style.removeProperty("--oh-story-seam-right");
4137
+ };
4138
+ }
3917
4139
  let composerSeat = () => scroller.querySelector(":scope > [data-composer-seat]"), parkedAtTail = () => scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight <= 8, parked = parkedAtTail(), drivenAt = 0, markDriven = (event) => {
3918
- event.target instanceof Node && composerSeat()?.contains(event.target) === !0 || (drivenAt = performance.now());
4140
+ event.target instanceof Node && composerSeat()?.contains(event.target) === !0 || event.type === "pointerdown" && event.target !== scroller || (drivenAt = performance.now());
3919
4141
  }, trackParked = () => {
3920
4142
  parkedAtTail() ? parked = !0 : performance.now() - drivenAt < 400 && (parked = !1);
3921
4143
  }, publishLayout = () => {
@@ -3947,7 +4169,7 @@ function CreativeSplitBridge({ sessionId, useSession, useChat, useStore, actions
3947
4169
  scroller.removeEventListener(driven, markDriven);
3948
4170
  observer.disconnect(), seats.disconnect(), scroller.style.removeProperty("--oh-story-scroll-height"), scroller.style.removeProperty("--oh-story-composer-height"), delete scroller.dataset.ohStoryLayout, delete scroller.dataset.ohStoryWorkbench, delete scroller.dataset.ohStudioPane;
3949
4171
  };
3950
- }, [gamePane, target, videoPane, workbench]), /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
4172
+ }, [gamePane, open, target, videoPane, workbench]), /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
3951
4173
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { ref: marker, className: "oh-story-bridge-marker", "aria-hidden": !0 }),
3952
4174
  target === void 0 ? null : (0, import_react_dom.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
3953
4175
  CreativeWorkbench,
@@ -3959,6 +4181,12 @@ function CreativeSplitBridge({ sessionId, useSession, useChat, useStore, actions
3959
4181
  sessionRunning,
3960
4182
  productionQueue,
3961
4183
  productionIntents,
4184
+ workspace,
4185
+ error,
4186
+ workspaceLoading,
4187
+ reload,
4188
+ open,
4189
+ creativeProject,
3962
4190
  sendProductionPrompt,
3963
4191
  cancelProduction,
3964
4192
  removeQueuedProduction,
@@ -3968,8 +4196,47 @@ function CreativeSplitBridge({ sessionId, useSession, useChat, useStore, actions
3968
4196
  ), target)
3969
4197
  ] });
3970
4198
  }
4199
+ function WorkbenchWelcome() {
4200
+ let marker = (0, import_react3.useRef)(null), [target, setTarget] = (0, import_react3.useState)();
4201
+ return (0, import_react3.useLayoutEffect)(() => {
4202
+ let document = marker.current?.ownerDocument;
4203
+ if (document === void 0) return;
4204
+ let locate = () => {
4205
+ let anchor = document.querySelector("[data-conversation-scroll]");
4206
+ setTarget((current) => current === anchor ? current : anchor ?? void 0);
4207
+ };
4208
+ locate();
4209
+ let observer = new MutationObserver(locate);
4210
+ return observer.observe(document.body, { childList: !0, subtree: !0 }), () => {
4211
+ observer.disconnect();
4212
+ };
4213
+ }, []), /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
4214
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("style", { children: plugin_default }),
4215
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { ref: marker, className: "oh-story-bridge-marker", "aria-hidden": !0 }),
4216
+ target === void 0 ? null : (0, import_react_dom.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("section", { className: "oh-story-welcome", "aria-label": "Oh Story \u4F7F\u7528\u5F15\u5BFC", children: [
4217
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { children: "Oh Story \u5DF2\u52A0\u8F7D" }),
4218
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: "\u4F5C\u54C1\u76EE\u5F55\u4E2D\u6709\u521B\u4F5C\u6587\u4EF6\u65F6\uFF0C\u5C0F\u8BF4\u3001\u77ED\u5267\u3001\u6E38\u620F\u3001\u89C6\u9891\u5DE5\u4F5C\u53F0\u4F1A\u81EA\u52A8\u663E\u793A\u3002" }),
4219
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("ol", { children: [
4220
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: "\u70B9\u51FB\u5DE6\u4FA7\u300C\u6DFB\u52A0\u5DE5\u4F5C\u533A / Add workspace\u300D\u7684 \uFF0B\uFF0C\u9009\u62E9\u5B58\u653E\u4F5C\u54C1\u7684\u6587\u4EF6\u5939\u3002" }),
4221
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: "\u5728\u4E0B\u65B9\u300C\u9009\u62E9\u5DE5\u4F5C\u533A / Choose workspace\u300D\u4E2D\u9009\u4E2D\u8BE5\u76EE\u5F55\uFF0C\u6216\u6253\u5F00\u5DF2\u6709\u4F1A\u8BDD\u3002" }),
4222
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("li", { children: "\u7A7A\u76EE\u5F55\u5148\u5728 Chat \u4E2D\u5F00\u59CB\u521B\u4F5C\uFF0C\u751F\u6210\u7B2C\u4E00\u4E2A\u521B\u4F5C\u6587\u4EF6\u540E\uFF0C\u5DE5\u4F5C\u53F0\u4F1A\u81EA\u52A8\u51FA\u73B0\u3002" })
4223
+ ] }),
4224
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("p", { children: [
4225
+ "\u67E5\u770B\u5DF2\u6709\u4F5C\u54C1\u65E0\u9700 API Key\u3002\u5F00\u59CB AI \u521B\u4F5C\u524D\uFF0C\u5728\u300C\u8BBE\u7F6E \u2192 \u6A21\u578B\u300D\u914D\u7F6E\u6A21\u578B\uFF0C\u518D\u8F93\u5165 ",
4226
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { children: "/story" }),
4227
+ "\u3001",
4228
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { children: "/short-drama" }),
4229
+ "\u3001",
4230
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { children: "/novel-to-game quick" }),
4231
+ " \u6216 ",
4232
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("code", { children: "/video-recap" }),
4233
+ "\u3002"
4234
+ ] })
4235
+ ] }), target)
4236
+ ] });
4237
+ }
3971
4238
  function WorkbenchSeat({ SessionProvider, renderSlot }) {
3972
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SessionProvider, { children: renderSlot("oh-story.workspace", {}) });
4239
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SessionProvider, { empty: () => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(WorkbenchWelcome, {}), children: renderSlot("oh-story.workspace", {}) });
3973
4240
  }
3974
4241
  function argsOf(block) {
3975
4242
  let raw = ("kind" in block ? block.call?.argsRaw : block.argsRaw) ?? "{}";