@open-press/cli 0.8.0 → 1.1.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 (250) hide show
  1. package/README.md +33 -23
  2. package/dist/cli.js +320 -252
  3. package/package.json +9 -8
  4. package/template/core/AGENTS.md +0 -126
  5. package/template/core/CHANGELOG.md +0 -215
  6. package/template/core/README.md +0 -40
  7. package/template/core/engine/cli.mjs +0 -96
  8. package/template/core/engine/commands/_shared.mjs +0 -177
  9. package/template/core/engine/commands/deploy.mjs +0 -31
  10. package/template/core/engine/commands/dev.mjs +0 -49
  11. package/template/core/engine/commands/doctor.mjs +0 -229
  12. package/template/core/engine/commands/export.mjs +0 -8
  13. package/template/core/engine/commands/init.mjs +0 -24
  14. package/template/core/engine/commands/inspect.mjs +0 -35
  15. package/template/core/engine/commands/pdf.mjs +0 -26
  16. package/template/core/engine/commands/preview.mjs +0 -26
  17. package/template/core/engine/commands/render.mjs +0 -17
  18. package/template/core/engine/commands/replace.mjs +0 -41
  19. package/template/core/engine/commands/search.mjs +0 -33
  20. package/template/core/engine/commands/typecheck.mjs +0 -5
  21. package/template/core/engine/commands/upgrade.mjs +0 -159
  22. package/template/core/engine/commands/validate.mjs +0 -17
  23. package/template/core/engine/document-export.mjs +0 -15
  24. package/template/core/engine/init.mjs +0 -90
  25. package/template/core/engine/output/chrome-pdf.d.mts +0 -34
  26. package/template/core/engine/output/chrome-pdf.mjs +0 -358
  27. package/template/core/engine/output/deploy-sync.mjs +0 -15
  28. package/template/core/engine/output/fonts.mjs +0 -62
  29. package/template/core/engine/output/katex-assets.mjs +0 -45
  30. package/template/core/engine/output/page-block.mjs +0 -30
  31. package/template/core/engine/output/pdf-media.mjs +0 -45
  32. package/template/core/engine/output/public-assets.mjs +0 -19
  33. package/template/core/engine/output/static-server.mjs +0 -532
  34. package/template/core/engine/react/caption-numbering.mjs +0 -73
  35. package/template/core/engine/react/comment-endpoint.d.mts +0 -11
  36. package/template/core/engine/react/comment-endpoint.mjs +0 -102
  37. package/template/core/engine/react/comment-marker.mjs +0 -374
  38. package/template/core/engine/react/document-entry.mjs +0 -324
  39. package/template/core/engine/react/document-export.mjs +0 -373
  40. package/template/core/engine/react/http-json.mjs +0 -24
  41. package/template/core/engine/react/mdx-compile.mjs +0 -599
  42. package/template/core/engine/react/measurement-css.mjs +0 -136
  43. package/template/core/engine/react/object-entities.mjs +0 -119
  44. package/template/core/engine/react/pagination/allocator.mjs +0 -122
  45. package/template/core/engine/react/pagination/regions.mjs +0 -81
  46. package/template/core/engine/react/pagination-constants.mjs +0 -3
  47. package/template/core/engine/react/pagination.mjs +0 -9
  48. package/template/core/engine/react/pipeline/allocate.mjs +0 -251
  49. package/template/core/engine/react/pipeline/final-render.mjs +0 -94
  50. package/template/core/engine/react/pipeline/frame-measurement.mjs +0 -302
  51. package/template/core/engine/react/pipeline/press-tree.mjs +0 -135
  52. package/template/core/engine/react/project-asset-endpoint.d.mts +0 -10
  53. package/template/core/engine/react/project-asset-endpoint.mjs +0 -361
  54. package/template/core/engine/react/section-css.mjs +0 -56
  55. package/template/core/engine/react/source-edit-endpoint.d.mts +0 -10
  56. package/template/core/engine/react/source-edit-endpoint.mjs +0 -75
  57. package/template/core/engine/react/sources/heading-numbering.mjs +0 -132
  58. package/template/core/engine/react/sources/mdx-resolver.mjs +0 -439
  59. package/template/core/engine/react/style-discovery.mjs +0 -142
  60. package/template/core/engine/runtime/config.d.mts +0 -40
  61. package/template/core/engine/runtime/config.mjs +0 -175
  62. package/template/core/engine/runtime/file-utils.mjs +0 -106
  63. package/template/core/engine/runtime/file-walk.mjs +0 -22
  64. package/template/core/engine/runtime/inspection.mjs +0 -328
  65. package/template/core/engine/runtime/issue-report.mjs +0 -44
  66. package/template/core/engine/runtime/path-utils.mjs +0 -20
  67. package/template/core/engine/runtime/source-text-tools.d.mts +0 -102
  68. package/template/core/engine/runtime/source-text-tools.mjs +0 -832
  69. package/template/core/engine/runtime/source-workspace.mjs +0 -159
  70. package/template/core/engine/runtime/validation.mjs +0 -174
  71. package/template/core/index.html +0 -13
  72. package/template/core/openpress.config.mjs +0 -12
  73. package/template/core/package.json +0 -91
  74. package/template/core/src/main.tsx +0 -16
  75. package/template/core/src/openpress/app/OpenPressApp.tsx +0 -140
  76. package/template/core/src/openpress/app/OpenPressRuntime.tsx +0 -94
  77. package/template/core/src/openpress/app/index.ts +0 -2
  78. package/template/core/src/openpress/core/Frame.tsx +0 -78
  79. package/template/core/src/openpress/core/FrameContext.tsx +0 -24
  80. package/template/core/src/openpress/core/MdxArea.tsx +0 -34
  81. package/template/core/src/openpress/core/Press.tsx +0 -34
  82. package/template/core/src/openpress/core/cn.ts +0 -4
  83. package/template/core/src/openpress/core/index.tsx +0 -40
  84. package/template/core/src/openpress/core/primitives.tsx +0 -44
  85. package/template/core/src/openpress/core/types.ts +0 -191
  86. package/template/core/src/openpress/core/useSource.ts +0 -28
  87. package/template/core/src/openpress/document-model/anchorMapModel.ts +0 -27
  88. package/template/core/src/openpress/document-model/documentIndexes.ts +0 -329
  89. package/template/core/src/openpress/document-model/documentTypes.ts +0 -138
  90. package/template/core/src/openpress/document-model/index.ts +0 -6
  91. package/template/core/src/openpress/document-model/objectEntityModel.ts +0 -51
  92. package/template/core/src/openpress/document-model/projectIdentityModel.ts +0 -15
  93. package/template/core/src/openpress/document-model/reactDocumentMetadataModel.ts +0 -27
  94. package/template/core/src/openpress/manuscript/index.tsx +0 -238
  95. package/template/core/src/openpress/mdx/index.ts +0 -88
  96. package/template/core/src/openpress/numbering/index.ts +0 -294
  97. package/template/core/src/openpress/reader/PublicReaderPage.tsx +0 -267
  98. package/template/core/src/openpress/reader/ReaderNavigationPanel.tsx +0 -123
  99. package/template/core/src/openpress/reader/index.ts +0 -10
  100. package/template/core/src/openpress/reader/pageViewportScaleModel.ts +0 -73
  101. package/template/core/src/openpress/reader/readerPageRegistry.ts +0 -41
  102. package/template/core/src/openpress/reader/readerPageRoute.ts +0 -21
  103. package/template/core/src/openpress/reader/readerScroll.ts +0 -92
  104. package/template/core/src/openpress/reader/readerStateModel.ts +0 -15
  105. package/template/core/src/openpress/reader/readerTypes.ts +0 -4
  106. package/template/core/src/openpress/reader/usePageViewportScale.ts +0 -119
  107. package/template/core/src/openpress/reader/usePanelState.ts +0 -56
  108. package/template/core/src/openpress/reader/useReaderHashSync.ts +0 -61
  109. package/template/core/src/openpress/reader/useReaderKeyboardNav.ts +0 -48
  110. package/template/core/src/openpress/reader/useReaderRuntime.ts +0 -146
  111. package/template/core/src/openpress/reader/useReaderScrollAnchor.ts +0 -64
  112. package/template/core/src/openpress/shared/Panel.tsx +0 -77
  113. package/template/core/src/openpress/shared/frameScheduler.ts +0 -32
  114. package/template/core/src/openpress/shared/index.ts +0 -4
  115. package/template/core/src/openpress/shared/numberUtils.ts +0 -3
  116. package/template/core/src/openpress/shared/runtimeMode.ts +0 -11
  117. package/template/core/src/openpress/workbench/Workbench.tsx +0 -407
  118. package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +0 -157
  119. package/template/core/src/openpress/workbench/actions/PageZoomControl.tsx +0 -182
  120. package/template/core/src/openpress/workbench/actions/SearchControl.tsx +0 -345
  121. package/template/core/src/openpress/workbench/actions/deploymentStatusModel.ts +0 -112
  122. package/template/core/src/openpress/workbench/actions/index.ts +0 -5
  123. package/template/core/src/openpress/workbench/actions/useDeploymentWorkbench.ts +0 -136
  124. package/template/core/src/openpress/workbench/dialog/WorkbenchDialog.tsx +0 -72
  125. package/template/core/src/openpress/workbench/dialog/index.ts +0 -1
  126. package/template/core/src/openpress/workbench/document/components/DocumentPanel.tsx +0 -127
  127. package/template/core/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +0 -207
  128. package/template/core/src/openpress/workbench/document/components/ReaderStage.tsx +0 -9
  129. package/template/core/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +0 -34
  130. package/template/core/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +0 -525
  131. package/template/core/src/openpress/workbench/document/index.ts +0 -10
  132. package/template/core/src/openpress/workbench/index.ts +0 -2
  133. package/template/core/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +0 -459
  134. package/template/core/src/openpress/workbench/inspector/index.ts +0 -5
  135. package/template/core/src/openpress/workbench/inspector/inlineCommentModel.ts +0 -125
  136. package/template/core/src/openpress/workbench/inspector/inspectorGeometryModel.ts +0 -160
  137. package/template/core/src/openpress/workbench/inspector/inspectorModel.ts +0 -408
  138. package/template/core/src/openpress/workbench/inspector/useInspectorComments.ts +0 -248
  139. package/template/core/src/openpress/workbench/mentions/MentionSuggestionList.tsx +0 -41
  140. package/template/core/src/openpress/workbench/mentions/index.ts +0 -2
  141. package/template/core/src/openpress/workbench/mentions/useComposerMentions.ts +0 -185
  142. package/template/core/src/openpress/workbench/panels/Panel.tsx +0 -1
  143. package/template/core/src/openpress/workbench/panels/PendingCommentsPanel.tsx +0 -76
  144. package/template/core/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +0 -29
  145. package/template/core/src/openpress/workbench/panels/index.ts +0 -3
  146. package/template/core/src/openpress/workbench/project/ProjectEntryPanel.tsx +0 -523
  147. package/template/core/src/openpress/workbench/project/ProjectPreviewDialog.tsx +0 -35
  148. package/template/core/src/openpress/workbench/project/index.ts +0 -2
  149. package/template/core/src/openpress/workbench/project/projectPreviewTypes.ts +0 -11
  150. package/template/core/src/openpress/workbench/project/projectSourceModel.ts +0 -24
  151. package/template/core/src/openpress/workbench/shell/WorkbenchShell.tsx +0 -167
  152. package/template/core/src/openpress/workbench/shell/index.ts +0 -1
  153. package/template/core/src/openpress/workbench/workbenchFormatters.ts +0 -120
  154. package/template/core/src/openpress/workbench/workbenchTypes.ts +0 -35
  155. package/template/core/src/styles/openpress/app-shell.css +0 -251
  156. package/template/core/src/styles/openpress/media-workspace.css +0 -230
  157. package/template/core/src/styles/openpress/print-route.css +0 -184
  158. package/template/core/src/styles/openpress/project-preview-panel.css +0 -924
  159. package/template/core/src/styles/openpress/public-viewer.css +0 -688
  160. package/template/core/src/styles/openpress/reader-runtime.css +0 -980
  161. package/template/core/src/styles/openpress/responsive.css +0 -245
  162. package/template/core/src/styles/openpress/workbench-panels.css +0 -594
  163. package/template/core/src/styles/openpress/workbench.css +0 -1255
  164. package/template/core/src/styles/openpress.css +0 -14
  165. package/template/core/src/vite-env.d.ts +0 -9
  166. package/template/core/tsconfig.json +0 -40
  167. package/template/core/vite.config.ts +0 -584
  168. package/template/packs/academic-paper/document/chapters/01-introduction/content/01-introduction.mdx +0 -35
  169. package/template/packs/academic-paper/document/chapters/02-methods/content/01-methods.mdx +0 -50
  170. package/template/packs/academic-paper/document/chapters/03-results-and-discussion/content/01-results.mdx +0 -47
  171. package/template/packs/academic-paper/document/chapters/04-acknowledgment/content/01-acknowledgment.mdx +0 -26
  172. package/template/packs/academic-paper/document/chapters/05-references/content/01-references.mdx +0 -32
  173. package/template/packs/academic-paper/document/components/ChapterOpenerVisual/index.tsx +0 -76
  174. package/template/packs/academic-paper/document/components/Page.tsx +0 -60
  175. package/template/packs/academic-paper/document/components/TokenSwatchGrid/index.tsx +0 -46
  176. package/template/packs/academic-paper/document/components/TokenSwatchGrid/style.css +0 -63
  177. package/template/packs/academic-paper/document/components/TypeSpecimen/index.tsx +0 -38
  178. package/template/packs/academic-paper/document/components/TypeSpecimen/style.css +0 -111
  179. package/template/packs/academic-paper/document/design.md +0 -279
  180. package/template/packs/academic-paper/document/index.tsx +0 -123
  181. package/template/packs/academic-paper/document/media/README.md +0 -13
  182. package/template/packs/academic-paper/document/media/figure-placeholder.svg +0 -9
  183. package/template/packs/academic-paper/document/openpress.config.mjs +0 -26
  184. package/template/packs/academic-paper/document/theme/README.md +0 -11
  185. package/template/packs/academic-paper/document/theme/base/page-contract.css +0 -522
  186. package/template/packs/academic-paper/document/theme/base/print.css +0 -93
  187. package/template/packs/academic-paper/document/theme/base/typography.css +0 -333
  188. package/template/packs/academic-paper/document/theme/fonts.css +0 -3
  189. package/template/packs/academic-paper/document/theme/page-surfaces/back-cover.css +0 -43
  190. package/template/packs/academic-paper/document/theme/page-surfaces/chapter-opener.css +0 -205
  191. package/template/packs/academic-paper/document/theme/page-surfaces/cover.css +0 -294
  192. package/template/packs/academic-paper/document/theme/page-surfaces/toc.css +0 -149
  193. package/template/packs/academic-paper/document/theme/patterns/_chart-frame.css +0 -49
  194. package/template/packs/academic-paper/document/theme/patterns/figure-grid.css +0 -68
  195. package/template/packs/academic-paper/document/theme/patterns/table-utilities.css +0 -66
  196. package/template/packs/academic-paper/document/theme/shell/reader-controls.css +0 -761
  197. package/template/packs/academic-paper/document/theme/tokens.css +0 -80
  198. package/template/packs/academic-paper/openpress.config.mjs +0 -5
  199. package/template/packs/claude-document/document/chapters/01-document-shape/content/01-document-shape.mdx +0 -51
  200. package/template/packs/claude-document/document/chapters/02-review-loop/content/01-review-loop.mdx +0 -31
  201. package/template/packs/claude-document/document/components/ChapterOpenerVisual.tsx +0 -96
  202. package/template/packs/claude-document/document/components/Page.tsx +0 -37
  203. package/template/packs/claude-document/document/design.md +0 -142
  204. package/template/packs/claude-document/document/index.tsx +0 -94
  205. package/template/packs/claude-document/document/media/README.md +0 -13
  206. package/template/packs/claude-document/document/openpress.config.mjs +0 -26
  207. package/template/packs/claude-document/document/theme/README.md +0 -15
  208. package/template/packs/claude-document/document/theme/base/page-contract.css +0 -525
  209. package/template/packs/claude-document/document/theme/base/print.css +0 -93
  210. package/template/packs/claude-document/document/theme/base/typography.css +0 -612
  211. package/template/packs/claude-document/document/theme/fonts.css +0 -4
  212. package/template/packs/claude-document/document/theme/page-surfaces/back-cover.css +0 -72
  213. package/template/packs/claude-document/document/theme/page-surfaces/chapter-opener.css +0 -236
  214. package/template/packs/claude-document/document/theme/page-surfaces/cover.css +0 -309
  215. package/template/packs/claude-document/document/theme/page-surfaces/toc.css +0 -225
  216. package/template/packs/claude-document/document/theme/patterns/_chart-frame.css +0 -53
  217. package/template/packs/claude-document/document/theme/patterns/figure-grid.css +0 -68
  218. package/template/packs/claude-document/document/theme/patterns/table-utilities.css +0 -66
  219. package/template/packs/claude-document/document/theme/shell/reader-controls.css +0 -789
  220. package/template/packs/claude-document/document/theme/tokens.css +0 -89
  221. package/template/packs/claude-document/openpress.config.mjs +0 -5
  222. package/template/packs/editorial-monograph/document/chapters/01-product-and-use-cases/content/01-product-and-use-cases.mdx +0 -31
  223. package/template/packs/editorial-monograph/document/chapters/02-workflow/content/01-workflow.mdx +0 -89
  224. package/template/packs/editorial-monograph/document/chapters/03-agent-skills-contributors/content/01-agent-skills-contributors.mdx +0 -51
  225. package/template/packs/editorial-monograph/document/chapters/04-validation-deploy/content/01-validation-deploy.mdx +0 -39
  226. package/template/packs/editorial-monograph/document/components/ChapterOpenerVisual/index.tsx +0 -76
  227. package/template/packs/editorial-monograph/document/components/Page.tsx +0 -37
  228. package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/index.tsx +0 -46
  229. package/template/packs/editorial-monograph/document/components/TokenSwatchGrid/style.css +0 -63
  230. package/template/packs/editorial-monograph/document/components/TypeSpecimen/index.tsx +0 -38
  231. package/template/packs/editorial-monograph/document/components/TypeSpecimen/style.css +0 -111
  232. package/template/packs/editorial-monograph/document/design.md +0 -279
  233. package/template/packs/editorial-monograph/document/index.tsx +0 -97
  234. package/template/packs/editorial-monograph/document/media/README.md +0 -13
  235. package/template/packs/editorial-monograph/document/openpress.config.mjs +0 -26
  236. package/template/packs/editorial-monograph/document/theme/README.md +0 -11
  237. package/template/packs/editorial-monograph/document/theme/base/page-contract.css +0 -505
  238. package/template/packs/editorial-monograph/document/theme/base/print.css +0 -93
  239. package/template/packs/editorial-monograph/document/theme/base/typography.css +0 -336
  240. package/template/packs/editorial-monograph/document/theme/fonts.css +0 -3
  241. package/template/packs/editorial-monograph/document/theme/page-surfaces/back-cover.css +0 -43
  242. package/template/packs/editorial-monograph/document/theme/page-surfaces/chapter-opener.css +0 -205
  243. package/template/packs/editorial-monograph/document/theme/page-surfaces/cover.css +0 -147
  244. package/template/packs/editorial-monograph/document/theme/page-surfaces/toc.css +0 -149
  245. package/template/packs/editorial-monograph/document/theme/patterns/_chart-frame.css +0 -49
  246. package/template/packs/editorial-monograph/document/theme/patterns/figure-grid.css +0 -68
  247. package/template/packs/editorial-monograph/document/theme/patterns/table-utilities.css +0 -66
  248. package/template/packs/editorial-monograph/document/theme/shell/reader-controls.css +0 -761
  249. package/template/packs/editorial-monograph/document/theme/tokens.css +0 -80
  250. package/template/packs/editorial-monograph/openpress.config.mjs +0 -5
@@ -1,248 +0,0 @@
1
- import { useCallback, useEffect, useMemo, useState, type FormEvent, type RefObject } from "react";
2
- import type { SourceBlock } from "../../document-model";
3
- import type { InlineSavedComment, InspectorCommentStatus, PendingCommentsStatus } from "../workbenchTypes";
4
- import { formatInspectorCommentStatus } from "../workbenchFormatters";
5
- import { clearInspectorComment, fetchInspectorComments } from "./inspectorModel";
6
- import { createInspectorCommentDraft, submitInspectorComment, updateInspectorComment } from "./inspectorModel";
7
- import type { InspectorState, PendingComment } from "./inspectorModel";
8
- import { getInlineSavedCommentForTarget, resolveInlineSavedComment } from "./inlineCommentModel";
9
-
10
- export interface UseInspectorCommentsOptions {
11
- devMode: boolean;
12
- inspector: InspectorState;
13
- sourceBlockMap: Record<string, SourceBlock>;
14
- sourceBlocksByPath: Record<string, SourceBlock[]>;
15
- sourceContainerRef: RefObject<HTMLDivElement | null>;
16
- onSelectWorkspacePage: (pageIndex: number, options?: { behavior?: ScrollBehavior }) => void;
17
- }
18
-
19
- export interface InspectorComments {
20
- pendingComments: PendingComment[];
21
- commentsStatus: PendingCommentsStatus;
22
- commentsError: string;
23
- inspectorCommentText: string;
24
- inspectorCommentStatus: InspectorCommentStatus;
25
- inspectorCommentStatusMessage: string;
26
- inspectorCommentDisabled: boolean;
27
- inlineSavedComments: InlineSavedComment[];
28
- activeInlineSavedComment: InlineSavedComment | null;
29
- setInspectorCommentText: (value: string) => void;
30
- refreshPendingComments: () => Promise<void>;
31
- clearPendingComment: (id: string) => Promise<void>;
32
- handleSubmitInspectorComment: (event?: FormEvent<HTMLFormElement>) => Promise<void>;
33
- handleOpenInlineSavedComment: (comment: InlineSavedComment) => void;
34
- handleRemoveInlineSavedComment: (comment: InlineSavedComment) => Promise<void>;
35
- handleSelectPendingComment: (comment: PendingComment) => void;
36
- }
37
-
38
- export function useInspectorComments({
39
- devMode,
40
- inspector,
41
- sourceBlockMap,
42
- sourceBlocksByPath,
43
- sourceContainerRef,
44
- onSelectWorkspacePage,
45
- }: UseInspectorCommentsOptions): InspectorComments {
46
- const [inspectorCommentText, setInspectorCommentText] = useState("");
47
- const [inspectorCommentStatus, setInspectorCommentStatus] = useState<InspectorCommentStatus>("idle");
48
- const [inspectorCommentError, setInspectorCommentError] = useState("");
49
- const [inlineSavedCommentId, setInlineSavedCommentId] = useState<string | null>(null);
50
- const [pendingComments, setPendingComments] = useState<PendingComment[]>([]);
51
- const [commentsStatus, setCommentsStatus] = useState<PendingCommentsStatus>("idle");
52
- const [commentsError, setCommentsError] = useState("");
53
-
54
- const inlineSavedComments = useMemo(
55
- () => pendingComments.flatMap((comment, index) => (
56
- resolveInlineSavedComment(comment, sourceBlocksByPath)
57
- .map((inlineComment) => ({ ...inlineComment, markerLabel: String(index + 1) }))
58
- )),
59
- [pendingComments, sourceBlocksByPath],
60
- );
61
-
62
- const activeInlineSavedComment = getInlineSavedCommentForTarget(
63
- inlineSavedComments,
64
- inspector.selectedTarget,
65
- inlineSavedCommentId,
66
- );
67
-
68
- const inspectorCommentDisabled =
69
- !inspector.selectedBlock || !inspectorCommentText.trim() || inspectorCommentStatus === "submitting";
70
- const inspectorCommentStatusMessage = formatInspectorCommentStatus(inspectorCommentStatus, inspectorCommentError);
71
-
72
- const refreshPendingComments = useCallback(async () => {
73
- if (!devMode) return;
74
- setCommentsStatus("loading");
75
- setCommentsError("");
76
- try {
77
- const comments = await fetchInspectorComments();
78
- setPendingComments(comments);
79
- setCommentsStatus("ready");
80
- } catch (error) {
81
- setCommentsStatus("failed");
82
- setCommentsError(error instanceof Error ? error.message : String(error));
83
- }
84
- }, [devMode]);
85
-
86
- const clearPendingComment = useCallback(async (id: string) => {
87
- setCommentsStatus("clearing");
88
- setCommentsError("");
89
- try {
90
- await clearInspectorComment({ id });
91
- setPendingComments((comments) => comments.filter((comment) => comment.id !== id));
92
- setInlineSavedCommentId((currentId) => (currentId === id ? null : currentId));
93
- setCommentsStatus("ready");
94
- } catch (error) {
95
- setCommentsStatus("failed");
96
- setCommentsError(error instanceof Error ? error.message : String(error));
97
- }
98
- }, []);
99
-
100
- const handleSubmitInspectorComment = useCallback(async (event?: FormEvent<HTMLFormElement>) => {
101
- event?.preventDefault();
102
- if (inspectorCommentDisabled || !inspector.selectedBlock) return;
103
- setInspectorCommentStatus("submitting");
104
- setInspectorCommentError("");
105
- try {
106
- const note = inspectorCommentText.trim();
107
- const placement = inspector.selectedTarget?.placement ?? "block";
108
- if (activeInlineSavedComment) {
109
- const result = await updateInspectorComment({
110
- id: activeInlineSavedComment.id,
111
- note,
112
- placement,
113
- });
114
- setInlineSavedCommentId(result.comment?.id ?? activeInlineSavedComment.id);
115
- } else {
116
- const draft = createInspectorCommentDraft({
117
- block: inspector.selectedBlock,
118
- entity: inspector.selectedObjectEntity,
119
- target: inspector.selectedTarget,
120
- note,
121
- placement,
122
- });
123
- const result = await submitInspectorComment({ draft });
124
- if (result.comment?.id) {
125
- setInlineSavedCommentId(result.comment.id);
126
- }
127
- }
128
- setInspectorCommentText("");
129
- setInspectorCommentStatus("saved");
130
- void refreshPendingComments();
131
- } catch (error) {
132
- setInspectorCommentStatus("failed");
133
- setInspectorCommentError(error instanceof Error ? error.message : String(error));
134
- }
135
- }, [
136
- activeInlineSavedComment,
137
- inspector.selectedBlock,
138
- inspector.selectedObjectEntity,
139
- inspector.selectedTarget,
140
- inspector.selectedTarget?.placement,
141
- inspectorCommentDisabled,
142
- inspectorCommentText,
143
- refreshPendingComments,
144
- ]);
145
-
146
- const handleOpenInlineSavedComment = useCallback((comment: InlineSavedComment) => {
147
- setInlineSavedCommentId(comment.id);
148
- setInspectorCommentText(comment.note);
149
- setInspectorCommentStatus("idle");
150
- setInspectorCommentError("");
151
- }, []);
152
-
153
- const handleRemoveInlineSavedComment = useCallback(async (comment: InlineSavedComment) => {
154
- setInspectorCommentStatus("submitting");
155
- setInspectorCommentError("");
156
- try {
157
- await clearInspectorComment({ id: comment.id });
158
- setPendingComments((comments) => comments.filter((item) => item.id !== comment.id));
159
- setInlineSavedCommentId((currentId) => (currentId === comment.id ? null : currentId));
160
- setInspectorCommentText("");
161
- setInspectorCommentStatus("idle");
162
- inspector.selectTarget(null);
163
- void refreshPendingComments();
164
- } catch (error) {
165
- setInspectorCommentStatus("failed");
166
- setInspectorCommentError(error instanceof Error ? error.message : String(error));
167
- }
168
- }, [inspector, refreshPendingComments]);
169
-
170
- const handleSelectPendingComment = useCallback((comment: PendingComment) => {
171
- const inlineComment = inlineSavedComments.find((item) => item.id === comment.id)
172
- ?? resolveInlineSavedComment(comment, sourceBlocksByPath)[0];
173
- if (!inlineComment?.blockId) return;
174
-
175
- const sourceBlock = sourceBlockMap[inlineComment.blockId];
176
- if (typeof sourceBlock?.pageIndex === "number") {
177
- onSelectWorkspacePage(sourceBlock.pageIndex, { behavior: "smooth" });
178
- }
179
-
180
- inspector.selectSelection({
181
- objectId: inlineComment.objectId,
182
- blockId: inlineComment.blockId,
183
- placement: inlineComment.placement,
184
- });
185
- handleOpenInlineSavedComment(inlineComment);
186
-
187
- window.requestAnimationFrame(() => {
188
- const selector = inlineComment.objectId
189
- ? `[data-openpress-object-id="${cssEscape(inlineComment.objectId)}"]`
190
- : `[data-openpress-block-id="${cssEscape(inlineComment.blockId!)}"]`;
191
- sourceContainerRef.current?.querySelector<HTMLElement>(selector)?.scrollIntoView({
192
- behavior: "smooth",
193
- block: "center",
194
- });
195
- });
196
- }, [
197
- handleOpenInlineSavedComment,
198
- inlineSavedComments,
199
- inspector,
200
- onSelectWorkspacePage,
201
- sourceBlockMap,
202
- sourceBlocksByPath,
203
- sourceContainerRef,
204
- ]);
205
-
206
- // Reset composer state when the inspector selection changes.
207
- useEffect(() => {
208
- setInspectorCommentStatus("idle");
209
- setInspectorCommentError("");
210
- setInspectorCommentText("");
211
- }, [inspector.selectedBlockId, inspector.selectedTarget?.placement]);
212
-
213
- // Drop the inline saved id if its comment is no longer reachable.
214
- useEffect(() => {
215
- if (inlineSavedCommentId && !activeInlineSavedComment) {
216
- setInlineSavedCommentId(null);
217
- }
218
- }, [activeInlineSavedComment, inlineSavedCommentId]);
219
-
220
- // Initial + dev-mode refresh of pending comments.
221
- useEffect(() => {
222
- if (!devMode) return;
223
- void refreshPendingComments();
224
- }, [devMode, refreshPendingComments]);
225
-
226
- return {
227
- pendingComments,
228
- commentsStatus,
229
- commentsError,
230
- inspectorCommentText,
231
- inspectorCommentStatus,
232
- inspectorCommentStatusMessage,
233
- inspectorCommentDisabled,
234
- inlineSavedComments,
235
- activeInlineSavedComment,
236
- setInspectorCommentText,
237
- refreshPendingComments,
238
- clearPendingComment,
239
- handleSubmitInspectorComment,
240
- handleOpenInlineSavedComment,
241
- handleRemoveInlineSavedComment,
242
- handleSelectPendingComment,
243
- };
244
- }
245
-
246
- function cssEscape(value: string) {
247
- return globalThis.CSS?.escape ? globalThis.CSS.escape(value) : value.replace(/["\\]/g, "\\$&");
248
- }
@@ -1,41 +0,0 @@
1
- import type { ComposerMentionItem } from "./useComposerMentions";
2
-
3
- export interface MentionSuggestionListProps {
4
- className: string;
5
- suggestions: ComposerMentionItem[];
6
- highlightedIndex: number;
7
- ariaLabel: string;
8
- onHighlight: (index: number) => void;
9
- onSelect: (item: ComposerMentionItem) => void;
10
- }
11
-
12
- export function MentionSuggestionList({
13
- className,
14
- suggestions,
15
- highlightedIndex,
16
- ariaLabel,
17
- onHighlight,
18
- onSelect,
19
- }: MentionSuggestionListProps) {
20
- if (suggestions.length === 0) return null;
21
-
22
- return (
23
- <div className={className} role="listbox" aria-label={ariaLabel}>
24
- {suggestions.map((item, index) => (
25
- <button
26
- type="button"
27
- role="option"
28
- aria-selected={index === highlightedIndex}
29
- data-highlighted={index === highlightedIndex ? "true" : undefined}
30
- key={`${item.kind}-${item.value}`}
31
- onMouseDown={(event) => event.preventDefault()}
32
- onMouseEnter={() => onHighlight(index)}
33
- onClick={() => onSelect(item)}
34
- >
35
- <span>{item.label}</span>
36
- <small>{item.meta}</small>
37
- </button>
38
- ))}
39
- </div>
40
- );
41
- }
@@ -1,2 +0,0 @@
1
- export * from "./useComposerMentions";
2
- export * from "./MentionSuggestionList";
@@ -1,185 +0,0 @@
1
- import { useEffect, useMemo, useState, type KeyboardEvent, type RefObject } from "react";
2
- import { clampNumber } from "../../shared";
3
-
4
- export type ComposerMentionItem = {
5
- trigger: "@" | "/";
6
- value: string;
7
- label: string;
8
- meta: string;
9
- kind: "media" | "component" | "skill" | "chapter" | "section" | "prefix";
10
- };
11
-
12
- export type ActiveComposerMention = {
13
- trigger: "@" | "/";
14
- query: string;
15
- start: number;
16
- end: number;
17
- };
18
-
19
- export function useComposerMentions({
20
- text,
21
- items,
22
- textareaRef,
23
- onTextChange,
24
- enabled = true,
25
- maxSuggestions = 7,
26
- }: {
27
- text: string;
28
- items: ComposerMentionItem[];
29
- textareaRef: RefObject<HTMLTextAreaElement | null>;
30
- onTextChange: (value: string) => void;
31
- enabled?: boolean;
32
- maxSuggestions?: number;
33
- }) {
34
- const [composerCursor, setComposerCursor] = useState(0);
35
- const [highlightedMentionIndex, setHighlightedMentionIndex] = useState(0);
36
- const [dismissedMentionKey, setDismissedMentionKey] = useState<string | null>(null);
37
- const activeMention = enabled ? resolveComposerMention(text, composerCursor) : null;
38
- const mentionKey = activeMention ? `${activeMention.trigger}:${activeMention.start}:${activeMention.query}` : null;
39
- const mentionSuggestions = useMemo(() => {
40
- if (!activeMention) return [];
41
- if (mentionKey && dismissedMentionKey === mentionKey) return [];
42
- return createMentionSuggestions(activeMention, items, maxSuggestions);
43
- }, [activeMention, dismissedMentionKey, items, maxSuggestions, mentionKey]);
44
-
45
- useEffect(() => {
46
- setHighlightedMentionIndex(0);
47
- }, [mentionKey, mentionSuggestions.length]);
48
-
49
- const syncCursor = () => {
50
- const textarea = textareaRef.current;
51
- if (textarea) setComposerCursor(textarea.selectionStart ?? text.length);
52
- };
53
-
54
- const insertMention = (item: ComposerMentionItem) => {
55
- if (!activeMention) return;
56
- const suffix = item.kind === "prefix" ? "" : " ";
57
- const nextText = `${text.slice(0, activeMention.start)}${item.value}${suffix}${text.slice(activeMention.end)}`;
58
- const nextCursor = activeMention.start + item.value.length + suffix.length;
59
- setDismissedMentionKey(null);
60
- onTextChange(nextText);
61
- if (typeof window === "undefined") return;
62
- window.requestAnimationFrame(() => {
63
- textareaRef.current?.focus();
64
- textareaRef.current?.setSelectionRange(nextCursor, nextCursor);
65
- setComposerCursor(nextCursor);
66
- });
67
- };
68
-
69
- const handleMentionKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {
70
- if (!activeMention || mentionSuggestions.length === 0) return false;
71
- if (event.key === "ArrowDown") {
72
- event.preventDefault();
73
- setHighlightedMentionIndex((index) => (index + 1) % mentionSuggestions.length);
74
- return true;
75
- }
76
- if (event.key === "ArrowUp") {
77
- event.preventDefault();
78
- setHighlightedMentionIndex((index) => (index - 1 + mentionSuggestions.length) % mentionSuggestions.length);
79
- return true;
80
- }
81
- if (event.key === "Enter" || event.key === "Tab") {
82
- event.preventDefault();
83
- insertMention(mentionSuggestions[highlightedMentionIndex] ?? mentionSuggestions[0]);
84
- return true;
85
- }
86
- if (event.key === "Escape") {
87
- event.preventDefault();
88
- setDismissedMentionKey(mentionKey);
89
- return true;
90
- }
91
- return false;
92
- };
93
-
94
- return {
95
- activeMention,
96
- handleMentionKeyDown,
97
- highlightedMentionIndex,
98
- setHighlightedMentionIndex,
99
- mentionSuggestions,
100
- setComposerCursor,
101
- syncCursor,
102
- insertMention,
103
- };
104
- }
105
-
106
- export function appendComposerToken(text: string, token: string) {
107
- const trimmedToken = token.trim();
108
- if (!trimmedToken) return text;
109
- if (!text.trim()) return `${trimmedToken} `;
110
- return `${text.replace(/\s*$/, " ")}${trimmedToken} `;
111
- }
112
-
113
- export function createMentionSuggestions(
114
- activeMention: ActiveComposerMention,
115
- items: ComposerMentionItem[],
116
- maxSuggestions: number,
117
- ) {
118
- if (activeMention.trigger === "@") {
119
- const prefixItems = createMentionPrefixItems(items);
120
- const normalizedQuery = activeMention.query.trim().toLowerCase();
121
- if (!normalizedQuery) return prefixItems.slice(0, maxSuggestions);
122
- if (!normalizedQuery.includes("/")) {
123
- return uniqueMentionItems([
124
- ...prefixItems.filter((item) => mentionMatches(item, normalizedQuery)),
125
- ...items.filter((item) => item.trigger === "@" && mentionMatches(item, normalizedQuery)),
126
- ]).slice(0, maxSuggestions);
127
- }
128
- }
129
-
130
- return items
131
- .filter((item) => item.trigger === activeMention.trigger && mentionMatches(item, activeMention.query))
132
- .slice(0, maxSuggestions);
133
- }
134
-
135
- function createMentionPrefixItems(items: ComposerMentionItem[]): ComposerMentionItem[] {
136
- const availableKinds = new Set(items.filter((item) => item.trigger === "@").map((item) => item.kind));
137
- return MENTION_PREFIX_DEFINITIONS
138
- .filter((item) => availableKinds.has(item.kind))
139
- .map((item) => ({
140
- trigger: "@" as const,
141
- value: `@${item.kind}/`,
142
- label: item.kind,
143
- meta: item.meta,
144
- kind: "prefix" as const,
145
- }));
146
- }
147
-
148
- function uniqueMentionItems(items: ComposerMentionItem[]) {
149
- const seen = new Set<string>();
150
- return items.filter((item) => {
151
- const key = `${item.kind}:${item.value}`;
152
- if (seen.has(key)) return false;
153
- seen.add(key);
154
- return true;
155
- });
156
- }
157
-
158
- function resolveComposerMention(text: string, cursor: number): ActiveComposerMention | null {
159
- const safeCursor = clampNumber(cursor, 0, text.length);
160
- const beforeCursor = text.slice(0, safeCursor);
161
- const match = beforeCursor.match(/(^|\s)([@/])([^\s@]*)$/);
162
- if (!match) return null;
163
- const start = beforeCursor.length - match[0].length + match[1].length;
164
- return {
165
- trigger: match[2] as "@" | "/",
166
- query: match[3] ?? "",
167
- start,
168
- end: safeCursor,
169
- };
170
- }
171
-
172
- function mentionMatches(item: ComposerMentionItem, query: string) {
173
- const normalizedQuery = query.trim().toLowerCase();
174
- if (!normalizedQuery) return true;
175
- return item.value.toLowerCase().includes(normalizedQuery)
176
- || item.label.toLowerCase().includes(normalizedQuery)
177
- || item.meta.toLowerCase().includes(normalizedQuery);
178
- }
179
-
180
- const MENTION_PREFIX_DEFINITIONS: Array<{ kind: "media" | "chapter" | "section" | "component"; meta: string }> = [
181
- { kind: "media", meta: "prefix · images" },
182
- { kind: "chapter", meta: "prefix · chapters" },
183
- { kind: "section", meta: "prefix · sections" },
184
- { kind: "component", meta: "prefix · components" },
185
- ];
@@ -1 +0,0 @@
1
- export { Panel } from "../../shared/Panel";
@@ -1,76 +0,0 @@
1
- import { Trash2 } from "lucide-react";
2
- import type { PendingComment } from "../inspector";
3
- import {
4
- formatCommentTimestamp,
5
- formatCommentsCount,
6
- } from "../workbenchFormatters";
7
- import type { PendingCommentsStatus } from "../workbenchTypes";
8
- import { Panel } from "./Panel";
9
-
10
- export function PendingCommentsPanel({
11
- comments,
12
- status,
13
- error,
14
- onClear,
15
- onSelect,
16
- }: {
17
- comments: PendingComment[];
18
- status: PendingCommentsStatus;
19
- error: string;
20
- onClear: (id: string) => Promise<void>;
21
- onSelect?: (comment: PendingComment) => void;
22
- }) {
23
- const busy = status === "loading" || status === "clearing";
24
-
25
- return (
26
- <Panel
27
- className="openpress-comments-panel openpress-comments-panel--embedded openpress-panel--compact"
28
- data-openpress-comments-panel
29
- aria-label="待處理註解"
30
- >
31
- <Panel.Header>
32
- <div className="openpress-panel-heading-stack">
33
- <Panel.Kicker>Comments</Panel.Kicker>
34
- <Panel.Title>待處理註解</Panel.Title>
35
- <Panel.Description>{formatCommentsCount(comments.length, status)}</Panel.Description>
36
- </div>
37
- </Panel.Header>
38
-
39
- <Panel.Body>
40
- {error ? <Panel.Error>{error}</Panel.Error> : null}
41
-
42
- {comments.length === 0 && status !== "loading" ? (
43
- <Panel.Empty role="status">目前沒有註解</Panel.Empty>
44
- ) : (
45
- <ol className="openpress-comments-list" aria-label="待處理註解列表">
46
- {comments.map((comment) => (
47
- <li className="openpress-comment-entry" data-openpress-comment-id={comment.id} key={comment.id}>
48
- <button
49
- type="button"
50
- className="openpress-comment-entry__jump"
51
- onClick={() => onSelect?.(comment)}
52
- aria-label={`跳到註解 ${comment.id}`}
53
- >
54
- <p className="openpress-comment-entry__note" title={comment.note}>{comment.note}</p>
55
- <p className="openpress-comment-entry__meta">
56
- <code>{comment.path}:{comment.line}</code>
57
- {comment.timestamp ? <span>{formatCommentTimestamp(comment.timestamp)}</span> : null}
58
- </p>
59
- </button>
60
- <button
61
- type="button"
62
- className="openpress-comment-entry__clear"
63
- onClick={() => void onClear(comment.id)}
64
- disabled={busy}
65
- aria-label={`清除註解 ${comment.id}`}
66
- >
67
- <Trash2 aria-hidden="true" />
68
- </button>
69
- </li>
70
- ))}
71
- </ol>
72
- )}
73
- </Panel.Body>
74
- </Panel>
75
- );
76
- }
@@ -1,29 +0,0 @@
1
- import { Fragment, type ReactNode } from "react";
2
-
3
- // A WorkbenchPanel is a self-contained renderable slot in the workbench
4
- // control surface. Each panel owns its own props by capturing them in render;
5
- // the host (HtmlWorkbench) supplies the list and decides ordering.
6
- export interface WorkbenchPanel {
7
- id: string;
8
- render: () => ReactNode;
9
- }
10
-
11
- export interface WorkbenchControlPanelProps {
12
- panels: WorkbenchPanel[];
13
- className?: string;
14
- ariaLabel?: string;
15
- }
16
-
17
- export function WorkbenchControlPanel({
18
- panels,
19
- className = "openpress-control-panel",
20
- ariaLabel = "控制面板",
21
- }: WorkbenchControlPanelProps) {
22
- return (
23
- <div className={className} data-openpress-control-panel aria-label={ariaLabel}>
24
- {panels.map((panel) => (
25
- <Fragment key={panel.id}>{panel.render()}</Fragment>
26
- ))}
27
- </div>
28
- );
29
- }
@@ -1,3 +0,0 @@
1
- export * from "./Panel";
2
- export * from "./PendingCommentsPanel";
3
- export * from "./WorkbenchControlPanel";