@open-press/cli 0.7.1 → 1.0.0

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 (234) hide show
  1. package/README.md +29 -13
  2. package/dist/cli.js +44 -195
  3. package/package.json +4 -5
  4. package/template/core/AGENTS.md +18 -14
  5. package/template/core/CHANGELOG.md +57 -9
  6. package/template/core/README.md +6 -3
  7. package/template/core/engine/cli.mjs +8 -8
  8. package/template/core/engine/commands/_shared.mjs +37 -15
  9. package/template/core/engine/commands/dev.mjs +2 -2
  10. package/template/core/engine/commands/image.mjs +29 -0
  11. package/template/core/engine/commands/skills-sync.mjs +71 -0
  12. package/template/core/engine/commands/typecheck.mjs +63 -1
  13. package/template/core/engine/commands/upgrade.mjs +3 -3
  14. package/template/core/engine/document-export.mjs +1 -1
  15. package/template/core/engine/output/chrome-pdf.mjs +110 -3
  16. package/template/core/engine/output/static-server.mjs +87 -9
  17. package/template/core/engine/react/comment-endpoint.mjs +13 -39
  18. package/template/core/engine/react/comment-marker.mjs +43 -19
  19. package/template/core/engine/react/document-entry.mjs +46 -28
  20. package/template/core/engine/react/document-export.mjs +328 -164
  21. package/template/core/engine/react/http-json.mjs +24 -0
  22. package/template/core/engine/react/mdx-compile.mjs +126 -3
  23. package/template/core/engine/react/measurement-css.mjs +114 -1
  24. package/template/core/engine/react/object-entities.mjs +204 -0
  25. package/template/core/engine/react/pagination/allocator.mjs +48 -3
  26. package/template/core/engine/react/pagination.mjs +1 -1
  27. package/template/core/engine/react/pipeline/allocate.mjs +41 -72
  28. package/template/core/engine/react/pipeline/frame-measurement.mjs +6 -0
  29. package/template/core/engine/react/press-tree-inspection.mjs +172 -0
  30. package/template/core/engine/react/project-asset-endpoint.mjs +6 -24
  31. package/template/core/engine/react/source-edit-endpoint.d.mts +10 -0
  32. package/template/core/engine/react/source-edit-endpoint.mjs +75 -0
  33. package/template/core/engine/react/sources/mdx-resolver.mjs +13 -15
  34. package/template/core/engine/react/style-discovery.mjs +23 -8
  35. package/template/core/engine/runtime/config.d.mts +8 -0
  36. package/template/core/engine/runtime/config.mjs +57 -60
  37. package/template/core/engine/runtime/file-utils.mjs +9 -1
  38. package/template/core/engine/runtime/file-walk.mjs +22 -0
  39. package/template/core/engine/runtime/inspection.mjs +1 -20
  40. package/template/core/engine/runtime/page-geometry.mjs +131 -0
  41. package/template/core/engine/runtime/path-utils.mjs +20 -0
  42. package/template/core/engine/runtime/source-text-tools.d.mts +102 -0
  43. package/template/core/engine/runtime/source-text-tools.mjs +551 -16
  44. package/template/core/engine/runtime/source-workspace.mjs +16 -34
  45. package/template/core/engine/runtime/validation.mjs +19 -10
  46. package/template/core/openpress.config.mjs +3 -7
  47. package/template/core/package.json +3 -5
  48. package/template/core/src/main.tsx +2 -2
  49. package/template/core/src/openpress/app/OpenPressApp.tsx +296 -0
  50. package/template/core/src/openpress/{renderer.tsx → app/OpenPressRuntime.tsx} +20 -9
  51. package/template/core/src/openpress/app/WorkspaceGalleryPage.tsx +219 -0
  52. package/template/core/src/openpress/app/index.ts +2 -0
  53. package/template/core/src/openpress/core/Frame.tsx +26 -15
  54. package/template/core/src/openpress/core/FrameContext.tsx +10 -3
  55. package/template/core/src/openpress/core/MdxArea.tsx +11 -12
  56. package/template/core/src/openpress/core/Press.tsx +25 -4
  57. package/template/core/src/openpress/core/Workspace.tsx +36 -0
  58. package/template/core/src/openpress/core/cn.ts +4 -0
  59. package/template/core/src/openpress/core/index.tsx +11 -3
  60. package/template/core/src/openpress/core/primitives.tsx +74 -6
  61. package/template/core/src/openpress/core/types.ts +94 -41
  62. package/template/core/src/openpress/core/useSource.ts +1 -1
  63. package/template/core/src/openpress/{anchorMap.ts → document-model/anchorMapModel.ts} +1 -1
  64. package/template/core/src/openpress/{indexes.ts → document-model/documentIndexes.ts} +1 -1
  65. package/template/core/src/openpress/{types.ts → document-model/documentTypes.ts} +51 -0
  66. package/template/core/src/openpress/document-model/index.ts +7 -0
  67. package/template/core/src/openpress/document-model/objectEntityModel.ts +55 -0
  68. package/template/core/src/openpress/{projectIdentity.ts → document-model/projectIdentityModel.ts} +1 -1
  69. package/template/core/src/openpress/{reactDocumentMetadata.ts → document-model/reactDocumentMetadataModel.ts} +1 -1
  70. package/template/core/src/openpress/document-model/workspaceManifestModel.ts +57 -0
  71. package/template/core/src/openpress/manuscript/index.tsx +49 -7
  72. package/template/core/src/openpress/mdx/index.ts +15 -7
  73. package/template/core/src/openpress/reader/PageThumbnailsPanel.tsx +168 -0
  74. package/template/core/src/openpress/{publicPage.tsx → reader/PublicReaderPage.tsx} +31 -51
  75. package/template/core/src/openpress/{workbenchPanels.tsx → reader/ReaderNavigationPanel.tsx} +6 -5
  76. package/template/core/src/openpress/reader/index.ts +11 -0
  77. package/template/core/src/openpress/reader/pageViewportScaleModel.ts +73 -0
  78. package/template/core/src/openpress/reader/readerTypes.ts +4 -0
  79. package/template/core/src/openpress/reader/usePageViewportScale.ts +119 -0
  80. package/template/core/src/openpress/reader/usePanelState.ts +56 -0
  81. package/template/core/src/openpress/reader/useReaderHashSync.ts +61 -0
  82. package/template/core/src/openpress/reader/useReaderKeyboardNav.ts +48 -0
  83. package/template/core/src/openpress/reader/useReaderRuntime.ts +146 -0
  84. package/template/core/src/openpress/reader/useReaderScrollAnchor.ts +64 -0
  85. package/template/core/src/openpress/shared/Panel.tsx +77 -0
  86. package/template/core/src/openpress/shared/index.ts +4 -0
  87. package/template/core/src/openpress/shared/numberUtils.ts +3 -0
  88. package/template/core/src/openpress/{runtimeMode.ts → shared/runtimeMode.ts} +0 -11
  89. package/template/core/src/openpress/workbench/Workbench.tsx +506 -0
  90. package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +157 -0
  91. package/template/core/src/openpress/workbench/actions/ExportImageControl.tsx +96 -0
  92. package/template/core/src/openpress/workbench/actions/PageZoomControl.tsx +182 -0
  93. package/template/core/src/openpress/workbench/actions/SearchControl.tsx +345 -0
  94. package/template/core/src/openpress/workbench/actions/deploymentStatusModel.ts +112 -0
  95. package/template/core/src/openpress/workbench/actions/index.ts +6 -0
  96. package/template/core/src/openpress/workbench/actions/useDeploymentWorkbench.ts +136 -0
  97. package/template/core/src/openpress/workbench/dialog/WorkbenchDialog.tsx +72 -0
  98. package/template/core/src/openpress/workbench/dialog/index.ts +1 -0
  99. package/template/core/src/openpress/workbench/document/components/DocumentPanel.tsx +127 -0
  100. package/template/core/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +207 -0
  101. package/template/core/src/openpress/workbench/document/components/ReaderStage.tsx +9 -0
  102. package/template/core/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +34 -0
  103. package/template/core/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +525 -0
  104. package/template/core/src/openpress/workbench/document/index.ts +10 -0
  105. package/template/core/src/openpress/workbench/index.ts +2 -0
  106. package/template/core/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +459 -0
  107. package/template/core/src/openpress/workbench/inspector/index.ts +5 -0
  108. package/template/core/src/openpress/workbench/inspector/inlineCommentModel.ts +125 -0
  109. package/template/core/src/openpress/workbench/inspector/inspectorGeometryModel.ts +160 -0
  110. package/template/core/src/openpress/workbench/inspector/inspectorModel.ts +408 -0
  111. package/template/core/src/openpress/workbench/inspector/useInspectorComments.ts +254 -0
  112. package/template/core/src/openpress/workbench/mentions/MentionSuggestionList.tsx +41 -0
  113. package/template/core/src/openpress/workbench/mentions/index.ts +2 -0
  114. package/template/core/src/openpress/{composerMentions.ts → workbench/mentions/useComposerMentions.ts} +1 -4
  115. package/template/core/src/openpress/workbench/panels/Panel.tsx +1 -0
  116. package/template/core/src/openpress/workbench/panels/PendingCommentsPanel.tsx +80 -0
  117. package/template/core/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +29 -0
  118. package/template/core/src/openpress/workbench/panels/index.ts +3 -0
  119. package/template/core/src/openpress/workbench/project/ProjectEntryPanel.tsx +525 -0
  120. package/template/core/src/openpress/workbench/project/ProjectPreviewDialog.tsx +35 -0
  121. package/template/core/src/openpress/workbench/project/index.ts +2 -0
  122. package/template/core/src/openpress/workbench/project/projectPreviewTypes.ts +11 -0
  123. package/template/core/src/openpress/workbench/shell/WorkbenchShell.tsx +167 -0
  124. package/template/core/src/openpress/workbench/shell/index.ts +1 -0
  125. package/template/core/src/openpress/workbench/workbenchFormatters.ts +120 -0
  126. package/template/core/src/openpress/workbench/workbenchTypes.ts +35 -0
  127. package/template/core/src/styles/openpress/print-route.css +0 -2
  128. package/template/core/src/styles/openpress/{project-workspace.css → project-preview-panel.css} +13 -407
  129. package/template/core/src/styles/openpress/public-viewer.css +25 -320
  130. package/template/core/src/styles/openpress/reader-runtime.css +252 -55
  131. package/template/core/src/styles/openpress/responsive.css +145 -270
  132. package/template/core/src/styles/openpress/workbench-panels.css +327 -178
  133. package/template/core/src/styles/openpress/workbench.css +986 -451
  134. package/template/core/src/styles/openpress/workspace-gallery.css +300 -0
  135. package/template/core/src/styles/openpress.css +2 -1
  136. package/template/core/tsconfig.json +1 -1
  137. package/template/core/vite.config.ts +50 -0
  138. package/template/core/engine/commands/init.mjs +0 -24
  139. package/template/core/engine/init.mjs +0 -90
  140. package/template/core/src/openpress/App.tsx +0 -127
  141. package/template/core/src/openpress/inspector.ts +0 -282
  142. package/template/core/src/openpress/projectWorkspace.tsx +0 -919
  143. package/template/core/src/openpress/readerRuntime.ts +0 -230
  144. package/template/core/src/openpress/workbench.tsx +0 -1265
  145. package/template/core/src/openpress/workbenchTypes.ts +0 -4
  146. package/template/packs/academic-paper/document/chapters/01-introduction/content/01-introduction.mdx +0 -35
  147. package/template/packs/academic-paper/document/chapters/02-methods/content/01-methods.mdx +0 -50
  148. package/template/packs/academic-paper/document/chapters/03-results-and-discussion/content/01-results.mdx +0 -47
  149. package/template/packs/academic-paper/document/chapters/04-acknowledgment/content/01-acknowledgment.mdx +0 -26
  150. package/template/packs/academic-paper/document/chapters/05-references/content/01-references.mdx +0 -32
  151. package/template/packs/academic-paper/document/components/ChapterOpenerVisual/index.tsx +0 -76
  152. package/template/packs/academic-paper/document/components/Page.tsx +0 -60
  153. package/template/packs/academic-paper/document/components/TokenSwatchGrid/index.tsx +0 -46
  154. package/template/packs/academic-paper/document/components/TokenSwatchGrid/style.css +0 -63
  155. package/template/packs/academic-paper/document/components/TypeSpecimen/index.tsx +0 -38
  156. package/template/packs/academic-paper/document/components/TypeSpecimen/style.css +0 -111
  157. package/template/packs/academic-paper/document/design.md +0 -279
  158. package/template/packs/academic-paper/document/index.tsx +0 -123
  159. package/template/packs/academic-paper/document/media/README.md +0 -13
  160. package/template/packs/academic-paper/document/media/figure-placeholder.svg +0 -9
  161. package/template/packs/academic-paper/document/openpress.config.mjs +0 -26
  162. package/template/packs/academic-paper/document/theme/README.md +0 -11
  163. package/template/packs/academic-paper/document/theme/base/page-contract.css +0 -522
  164. package/template/packs/academic-paper/document/theme/base/print.css +0 -93
  165. package/template/packs/academic-paper/document/theme/base/typography.css +0 -333
  166. package/template/packs/academic-paper/document/theme/fonts.css +0 -3
  167. package/template/packs/academic-paper/document/theme/page-surfaces/back-cover.css +0 -43
  168. package/template/packs/academic-paper/document/theme/page-surfaces/chapter-opener.css +0 -205
  169. package/template/packs/academic-paper/document/theme/page-surfaces/cover.css +0 -294
  170. package/template/packs/academic-paper/document/theme/page-surfaces/toc.css +0 -149
  171. package/template/packs/academic-paper/document/theme/patterns/_chart-frame.css +0 -49
  172. package/template/packs/academic-paper/document/theme/patterns/figure-grid.css +0 -68
  173. package/template/packs/academic-paper/document/theme/patterns/table-utilities.css +0 -66
  174. package/template/packs/academic-paper/document/theme/shell/reader-controls.css +0 -761
  175. package/template/packs/academic-paper/document/theme/tokens.css +0 -80
  176. package/template/packs/academic-paper/openpress.config.mjs +0 -5
  177. package/template/packs/claude-document/document/chapters/01-document-shape/content/01-document-shape.mdx +0 -51
  178. package/template/packs/claude-document/document/chapters/02-review-loop/content/01-review-loop.mdx +0 -31
  179. package/template/packs/claude-document/document/components/ChapterOpenerVisual.tsx +0 -96
  180. package/template/packs/claude-document/document/components/Page.tsx +0 -37
  181. package/template/packs/claude-document/document/design.md +0 -142
  182. package/template/packs/claude-document/document/index.tsx +0 -94
  183. package/template/packs/claude-document/document/media/README.md +0 -13
  184. package/template/packs/claude-document/document/openpress.config.mjs +0 -26
  185. package/template/packs/claude-document/document/theme/README.md +0 -15
  186. package/template/packs/claude-document/document/theme/base/page-contract.css +0 -525
  187. package/template/packs/claude-document/document/theme/base/print.css +0 -93
  188. package/template/packs/claude-document/document/theme/base/typography.css +0 -612
  189. package/template/packs/claude-document/document/theme/fonts.css +0 -4
  190. package/template/packs/claude-document/document/theme/page-surfaces/back-cover.css +0 -72
  191. package/template/packs/claude-document/document/theme/page-surfaces/chapter-opener.css +0 -236
  192. package/template/packs/claude-document/document/theme/page-surfaces/cover.css +0 -309
  193. package/template/packs/claude-document/document/theme/page-surfaces/toc.css +0 -225
  194. package/template/packs/claude-document/document/theme/patterns/_chart-frame.css +0 -53
  195. package/template/packs/claude-document/document/theme/patterns/figure-grid.css +0 -68
  196. package/template/packs/claude-document/document/theme/patterns/table-utilities.css +0 -66
  197. package/template/packs/claude-document/document/theme/shell/reader-controls.css +0 -789
  198. package/template/packs/claude-document/document/theme/tokens.css +0 -89
  199. package/template/packs/claude-document/openpress.config.mjs +0 -5
  200. package/template/packs/editorial-monograph/document/chapters/01-product-and-use-cases/content/01-product-and-use-cases.mdx +0 -31
  201. package/template/packs/editorial-monograph/document/chapters/02-workflow/content/01-workflow.mdx +0 -89
  202. package/template/packs/editorial-monograph/document/chapters/03-agent-skills-contributors/content/01-agent-skills-contributors.mdx +0 -51
  203. package/template/packs/editorial-monograph/document/chapters/04-validation-deploy/content/01-validation-deploy.mdx +0 -39
  204. package/template/packs/editorial-monograph/document/components/ChapterOpenerVisual/index.tsx +0 -76
  205. package/template/packs/editorial-monograph/document/components/Page.tsx +0 -37
  206. package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/index.tsx +0 -46
  207. package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/style.css +0 -63
  208. package/template/packs/editorial-monograph/document/components/TypeSpecimen/index.tsx +0 -38
  209. package/template/packs/editorial-monograph/document/components/TypeSpecimen/style.css +0 -111
  210. package/template/packs/editorial-monograph/document/design.md +0 -279
  211. package/template/packs/editorial-monograph/document/index.tsx +0 -97
  212. package/template/packs/editorial-monograph/document/media/README.md +0 -13
  213. package/template/packs/editorial-monograph/document/openpress.config.mjs +0 -26
  214. package/template/packs/editorial-monograph/document/theme/README.md +0 -11
  215. package/template/packs/editorial-monograph/document/theme/base/page-contract.css +0 -505
  216. package/template/packs/editorial-monograph/document/theme/base/print.css +0 -93
  217. package/template/packs/editorial-monograph/document/theme/base/typography.css +0 -336
  218. package/template/packs/editorial-monograph/document/theme/fonts.css +0 -3
  219. package/template/packs/editorial-monograph/document/theme/page-surfaces/back-cover.css +0 -43
  220. package/template/packs/editorial-monograph/document/theme/page-surfaces/chapter-opener.css +0 -205
  221. package/template/packs/editorial-monograph/document/theme/page-surfaces/cover.css +0 -147
  222. package/template/packs/editorial-monograph/document/theme/page-surfaces/toc.css +0 -149
  223. package/template/packs/editorial-monograph/document/theme/patterns/_chart-frame.css +0 -49
  224. package/template/packs/editorial-monograph/document/theme/patterns/figure-grid.css +0 -68
  225. package/template/packs/editorial-monograph/document/theme/patterns/table-utilities.css +0 -66
  226. package/template/packs/editorial-monograph/document/theme/shell/reader-controls.css +0 -761
  227. package/template/packs/editorial-monograph/document/theme/tokens.css +0 -80
  228. package/template/packs/editorial-monograph/openpress.config.mjs +0 -5
  229. /package/template/core/src/openpress/{readerPageRegistry.ts → reader/readerPageRegistry.ts} +0 -0
  230. /package/template/core/src/openpress/{pageRoute.ts → reader/readerPageRoute.ts} +0 -0
  231. /package/template/core/src/openpress/{readerScroll.ts → reader/readerScroll.ts} +0 -0
  232. /package/template/core/src/openpress/{readerState.ts → reader/readerStateModel.ts} +0 -0
  233. /package/template/core/src/openpress/{frameScheduler.ts → shared/frameScheduler.ts} +0 -0
  234. /package/template/core/src/openpress/{projectSources.ts → workbench/project/projectSourceModel.ts} +0 -0
@@ -0,0 +1,167 @@
1
+ import { createContext, useContext, type CSSProperties, type ReactNode } from "react";
2
+ import { PanelLeftClose, PanelLeftOpen, PanelRightClose, PanelRightOpen, X } from "lucide-react";
3
+
4
+ type WorkbenchShellContextValue = {
5
+ leftPanelOpen: boolean;
6
+ rightPanelOpen: boolean;
7
+ onToggleLeftPanel: () => void;
8
+ onToggleRightPanel: () => void;
9
+ };
10
+
11
+ const WorkbenchShellContext = createContext<WorkbenchShellContextValue | null>(null);
12
+
13
+ function useWorkbenchShell() {
14
+ const value = useContext(WorkbenchShellContext);
15
+ if (!value) throw new Error("WorkbenchShell compound components must be rendered inside <WorkbenchShell>.");
16
+ return value;
17
+ }
18
+
19
+ function WorkbenchShellRoot({
20
+ style,
21
+ devMode,
22
+ viewMode,
23
+ inspectorMode,
24
+ editMode = false,
25
+ leftPanelOpen,
26
+ rightPanelOpen,
27
+ onToggleLeftPanel,
28
+ onToggleRightPanel,
29
+ children,
30
+ }: {
31
+ style: CSSProperties;
32
+ devMode: boolean;
33
+ viewMode: string;
34
+ inspectorMode: boolean;
35
+ editMode?: boolean;
36
+ leftPanelOpen: boolean;
37
+ rightPanelOpen: boolean;
38
+ onToggleLeftPanel: () => void;
39
+ onToggleRightPanel: () => void;
40
+ children: ReactNode;
41
+ }) {
42
+ const scrimOpen = leftPanelOpen || rightPanelOpen;
43
+ const handleScrimClick = rightPanelOpen ? onToggleRightPanel : onToggleLeftPanel;
44
+ const shellClassName = [
45
+ "reader-app openpress-reader-app openpress-public-viewer openpress-dev-public-viewer openpress-workbench-shell is-ready",
46
+ leftPanelOpen ? "" : "is-closed-left",
47
+ rightPanelOpen ? "" : "is-closed-right",
48
+ ].filter(Boolean).join(" ");
49
+
50
+ return (
51
+ <WorkbenchShellContext.Provider value={{ leftPanelOpen, rightPanelOpen, onToggleLeftPanel, onToggleRightPanel }}>
52
+ <main className="openpress-workbench" style={style} data-dev-mode={devMode ? "true" : "false"}>
53
+ <div
54
+ className={shellClassName}
55
+ data-openpress-react-runtime="true"
56
+ data-openpress-view-mode={viewMode}
57
+ data-openpress-inspector-mode={inspectorMode ? "on" : "off"}
58
+ data-openpress-edit-mode={editMode ? "on" : "off"}
59
+ data-openpress-workbench-shell
60
+ data-testid="workbench-shell"
61
+ >
62
+ {scrimOpen ? (
63
+ <div className="openpress-public-scrim" aria-hidden="true" onClick={handleScrimClick} />
64
+ ) : null}
65
+ {children}
66
+ </div>
67
+ </main>
68
+ </WorkbenchShellContext.Provider>
69
+ );
70
+ }
71
+
72
+ export function WorkbenchToolbar({ children }: { children: ReactNode }) {
73
+ const {
74
+ leftPanelOpen,
75
+ rightPanelOpen,
76
+ onToggleLeftPanel,
77
+ onToggleRightPanel,
78
+ } = useWorkbenchShell();
79
+ const LeftIcon = leftPanelOpen ? PanelLeftClose : PanelLeftOpen;
80
+ const RightIcon = rightPanelOpen ? PanelRightClose : PanelRightOpen;
81
+ const leftLabel = leftPanelOpen ? "收合左側面板" : "展開左側面板";
82
+ const rightLabel = rightPanelOpen ? "收合右側面板" : "展開右側面板";
83
+
84
+ return (
85
+ <header
86
+ className="openpress-workbench-toolbar"
87
+ role="toolbar"
88
+ aria-label="工作台操作"
89
+ data-openpress-workbench-toolbar
90
+ >
91
+ <button
92
+ type="button"
93
+ className="openpress-workbench-toolbar-panel-toggle"
94
+ data-openpress-toggle-left-panel
95
+ data-openpress-panel-open={leftPanelOpen ? "true" : "false"}
96
+ aria-label={leftLabel}
97
+ title={leftLabel}
98
+ onClick={onToggleLeftPanel}
99
+ >
100
+ <LeftIcon aria-hidden="true" />
101
+ </button>
102
+ <div className="openpress-workbench-toolbar__content">
103
+ {children}
104
+ </div>
105
+ <button
106
+ type="button"
107
+ className="openpress-workbench-toolbar-panel-toggle"
108
+ data-openpress-toggle-right-panel
109
+ data-openpress-panel-open={rightPanelOpen ? "true" : "false"}
110
+ aria-label={rightLabel}
111
+ title={rightLabel}
112
+ onClick={onToggleRightPanel}
113
+ >
114
+ <RightIcon aria-hidden="true" />
115
+ </button>
116
+ </header>
117
+ );
118
+ }
119
+
120
+ function WorkbenchLeftPanel({ children }: { children: ReactNode }) {
121
+ return (
122
+ <aside
123
+ className="reader-side-nav openpress-workspace-panel openpress-workbench-left-panel openpress-public-navigation"
124
+ aria-label="文件導覽"
125
+ data-openpress-left-panel
126
+ >
127
+ {children}
128
+ </aside>
129
+ );
130
+ }
131
+
132
+ function WorkbenchRightPanel({ children }: { children: ReactNode }) {
133
+ const { onToggleRightPanel } = useWorkbenchShell();
134
+
135
+ return (
136
+ <aside
137
+ className="openpress-workspace-panel openpress-workbench-right-panel openpress-dev-public-navigation"
138
+ aria-label="控制面板"
139
+ data-openpress-right-panel
140
+ >
141
+ <button type="button" className="openpress-public-drawer-close" aria-label="關閉右側面板" onClick={onToggleRightPanel}>
142
+ <X size={16} aria-hidden="true" />
143
+ </button>
144
+ {children}
145
+ </aside>
146
+ );
147
+ }
148
+
149
+ function WorkbenchMainContent({ children }: { children: ReactNode }) {
150
+ return (
151
+ <section
152
+ className="openpress-workbench__stage openpress-workbench-main openpress-public-viewer__stage openpress-dev-main-content"
153
+ aria-label="主要內容"
154
+ data-openpress-main-content
155
+ >
156
+ {children}
157
+ </section>
158
+ );
159
+ }
160
+
161
+ export const WorkbenchShell = Object.assign(WorkbenchShellRoot, {
162
+ Toolbar: WorkbenchToolbar,
163
+ LeftPanel: WorkbenchLeftPanel,
164
+ RightPanel: WorkbenchRightPanel,
165
+ ControlPanel: WorkbenchRightPanel,
166
+ MainContent: WorkbenchMainContent,
167
+ });
@@ -0,0 +1 @@
1
+ export * from "./WorkbenchShell";
@@ -0,0 +1,120 @@
1
+ import type { InspectorPlacement } from "./inspector";
2
+ import type { ObjectEntity, SourceBlock, Theme } from "../document-model";
3
+ import type { PendingCommentsStatus, InspectorCommentStatus } from "./workbenchTypes";
4
+
5
+ export type PageGeometrySpec = {
6
+ label: string;
7
+ dimensions: string;
8
+ title: string;
9
+ };
10
+
11
+ const DEFAULT_PAGE_GEOMETRY = {
12
+ pageWidth: "210mm",
13
+ pageHeight: "297mm",
14
+ };
15
+
16
+ export function formatInspectorSelection(block: SourceBlock | null, entity?: ObjectEntity | null) {
17
+ if (entity) return entity.label;
18
+ if (!block) return "未選取";
19
+ const line = block.source?.line;
20
+ return line ? `${block.path}:${line}` : block.path;
21
+ }
22
+
23
+ export function formatInspectorCommentStatus(status: InspectorCommentStatus, error: string) {
24
+ if (status === "submitting") return "寫入中";
25
+ if (status === "saved") return "已寫入 source";
26
+ if (status === "failed") return error || "寫入失敗";
27
+ return "";
28
+ }
29
+
30
+ export function formatCommentsCount(count: number, status: PendingCommentsStatus) {
31
+ if (status === "loading") return "正在讀取";
32
+ if (status === "clearing") return "正在清除";
33
+ return `${count} 則待處理`;
34
+ }
35
+
36
+ export function formatPageGeometrySpec(theme?: Pick<Theme, "pageLabel" | "pageWidth" | "pageHeight">): PageGeometrySpec {
37
+ const width = parseCssLength(theme?.pageWidth ?? DEFAULT_PAGE_GEOMETRY.pageWidth);
38
+ const height = parseCssLength(theme?.pageHeight ?? DEFAULT_PAGE_GEOMETRY.pageHeight);
39
+ const dimensions = formatLengthPair(width, height);
40
+ const label = theme?.pageLabel?.trim() || pageGeometryLabel(width, height);
41
+
42
+ return {
43
+ label,
44
+ dimensions,
45
+ title: `${label} · ${dimensions}`,
46
+ };
47
+ }
48
+
49
+ export function parseCommentHint(hint?: string) {
50
+ if (!hint?.startsWith("openpress-react-inspector")) return null;
51
+ const intent = hint.match(/\bintent=(add|edit|delete)\b/)?.[1];
52
+ const placement = hint.match(/\bplacement=(block|before)\b/)?.[1] as InspectorPlacement | undefined;
53
+ const targetObjectId = decodeHintValue(hint.match(/\btarget=([^\s]+)/)?.[1]);
54
+ const intentLabel = intent === "add" ? "Add" : intent === "delete" ? "Remove" : "Edit";
55
+ const placementLabel = placement === "before" ? "插入於區塊前" : "針對目前區塊";
56
+ return { intent: intent ?? "edit", intentLabel, placement: placement ?? "block", placementLabel, targetObjectId };
57
+ }
58
+
59
+ export function formatCommentTimestamp(value: string) {
60
+ const date = new Date(value);
61
+ if (Number.isNaN(date.getTime())) return value;
62
+ return new Intl.DateTimeFormat("zh-TW", {
63
+ month: "2-digit",
64
+ day: "2-digit",
65
+ hour: "2-digit",
66
+ minute: "2-digit",
67
+ hour12: false,
68
+ }).format(date);
69
+ }
70
+
71
+ function pageGeometryLabel(width: CssLength, height: CssLength) {
72
+ if (matchesPageSize(width, height, "210", "297", "mm")) return "A4 Page";
73
+ if (matchesPageSize(width, height, "176", "250", "mm")) return "B5 Page";
74
+ if (matchesPageSize(width, height, "215.9", "279.4", "mm")) return "Letter Page";
75
+ if (isSixteenByNine(width, height)) return "16:9 Page";
76
+ return "Custom Page";
77
+ }
78
+
79
+ type CssLength = {
80
+ raw: string;
81
+ value: string | null;
82
+ unit: string | null;
83
+ };
84
+
85
+ function parseCssLength(value: string): CssLength {
86
+ const raw = value.trim();
87
+ const match = raw.match(/^(-?\d+(?:\.\d+)?)([a-z%]+)$/i);
88
+ if (!match) return { raw, value: null, unit: null };
89
+ return { raw, value: trimTrailingZeroes(match[1]), unit: match[2] };
90
+ }
91
+
92
+ function formatLengthPair(width: CssLength, height: CssLength) {
93
+ if (width.value && height.value && width.unit && width.unit === height.unit) {
94
+ return `${width.value} × ${height.value} ${width.unit}`;
95
+ }
96
+ return `${width.raw} × ${height.raw}`;
97
+ }
98
+
99
+ function matchesPageSize(width: CssLength, height: CssLength, targetWidth: string, targetHeight: string, unit: string) {
100
+ return width.value === targetWidth && height.value === targetHeight && width.unit === unit && height.unit === unit;
101
+ }
102
+
103
+ function isSixteenByNine(width: CssLength, height: CssLength) {
104
+ if (!width.value || !height.value || !width.unit || width.unit !== height.unit) return false;
105
+ const ratio = Number(width.value) / Number(height.value);
106
+ return ratio > 1 && Math.abs(ratio - (16 / 9)) < 0.02;
107
+ }
108
+
109
+ function decodeHintValue(value?: string) {
110
+ if (!value) return undefined;
111
+ try {
112
+ return decodeURIComponent(value);
113
+ } catch {
114
+ return value;
115
+ }
116
+ }
117
+
118
+ function trimTrailingZeroes(value: string) {
119
+ return value.replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
120
+ }
@@ -0,0 +1,35 @@
1
+ import type { InspectorPlacement, ObjectSelection } from "./inspector/inspectorModel";
2
+
3
+ export type DeployStatus = "idle" | "deploying" | "deployed" | "unavailable" | "failed" | "setup";
4
+ export type PdfActionStatus = "idle" | "generating" | "opening" | "failed";
5
+ export type InspectorCommentStatus = "idle" | "submitting" | "saved" | "failed";
6
+ export type PendingCommentsStatus = "idle" | "loading" | "ready" | "failed" | "clearing";
7
+
8
+ export interface InlineSavedComment {
9
+ id: string;
10
+ objectId?: string;
11
+ blockId?: string;
12
+ placement: InspectorPlacement;
13
+ note: string;
14
+ path?: string;
15
+ line?: number;
16
+ timestamp?: string;
17
+ markerLabel?: string;
18
+ }
19
+
20
+ export interface InlineSavedCommentMarkerEntry {
21
+ target: ObjectSelection;
22
+ comments: InlineSavedComment[];
23
+ }
24
+
25
+ export interface InspectorLayerRect {
26
+ top: number;
27
+ left: number;
28
+ width: number;
29
+ height: number;
30
+ }
31
+
32
+ export interface InspectorInsertTargetView {
33
+ blockId: string;
34
+ rect: InspectorLayerRect;
35
+ }
@@ -46,8 +46,6 @@
46
46
 
47
47
  .reader-navbar,
48
48
  .reader-side-nav,
49
- .reader-thumb-strip,
50
- .openpress-workspace-navbar,
51
49
  .openpress-workspace-panel,
52
50
  .openpress-public-identity,
53
51
  .openpress-html-page__toolbar {