@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,459 +0,0 @@
1
- import {
2
- memo,
3
- useCallback,
4
- useEffect,
5
- useLayoutEffect,
6
- useMemo,
7
- useRef,
8
- useState,
9
- type FormEvent,
10
- type RefObject,
11
- } from "react";
12
- import { ArrowUp, Pencil, Plus, Trash2 } from "lucide-react";
13
- import { MentionSuggestionList, useComposerMentions } from "../mentions";
14
- import type {
15
- InspectorState,
16
- ObjectSelection,
17
- } from "./inspectorModel";
18
- import type { ProjectMentionItem } from "../project";
19
- import {
20
- collectInspectorBlockElements,
21
- createInspectorComposerStyle,
22
- createInspectorInsertTargets,
23
- createInspectorMarkerStyle,
24
- rectToFixedStyle,
25
- resolveInspectorSelectionRect,
26
- syncInspectorSelectedBlock,
27
- } from "./inspectorGeometryModel";
28
- import {
29
- getInlineSavedCommentForTarget,
30
- getInlineSavedCommentMarkers,
31
- } from "./inlineCommentModel";
32
- import type {
33
- InlineSavedComment,
34
- InlineSavedCommentMarkerEntry,
35
- InspectorCommentStatus,
36
- InspectorInsertTargetView,
37
- InspectorLayerRect,
38
- } from "../workbenchTypes";
39
-
40
- type ComposerAction = "add" | "edit" | "delete";
41
-
42
- const COMPOSER_ACTIONS: Array<{ action: ComposerAction; label: string; icon: typeof Plus; prefix: string }> = [
43
- { action: "add", label: "Add", icon: Plus, prefix: "請新增:" },
44
- { action: "edit", label: "Edit", icon: Pencil, prefix: "請修改:" },
45
- { action: "delete", label: "Remove", icon: Trash2, prefix: "請刪除這個物件。" },
46
- ];
47
-
48
- export interface InlineCommentController {
49
- saved: InlineSavedComment[];
50
- active: InlineSavedComment | null;
51
- status: InspectorCommentStatus;
52
- statusMessage: string;
53
- totalCount?: number;
54
- onOpenSaved: (comment: InlineSavedComment) => void;
55
- onRemoveSaved: (comment: InlineSavedComment) => Promise<void>;
56
- }
57
-
58
- export interface InlineComposerController {
59
- text: string;
60
- submitDisabled: boolean;
61
- mentionItems: ProjectMentionItem[];
62
- onTextChange: (value: string) => void;
63
- onSubmit: (event?: FormEvent<HTMLFormElement>) => Promise<void>;
64
- }
65
-
66
- export interface InlineInspectorLayerProps {
67
- sourceContainerRef: RefObject<HTMLDivElement | null>;
68
- inspector: InspectorState;
69
- comments: InlineCommentController;
70
- composer: InlineComposerController;
71
- geometryVersion?: unknown;
72
- }
73
-
74
- function InlineInspectorLayerImpl({
75
- sourceContainerRef,
76
- inspector,
77
- comments,
78
- composer,
79
- geometryVersion,
80
- }: InlineInspectorLayerProps) {
81
- const savedComments = comments.saved;
82
- const savedCommentTotalCount = comments.totalCount ?? savedComments.length;
83
- const activeSavedComment = comments.active;
84
- const commentText = composer.text;
85
- const commentStatus = comments.status;
86
- const commentStatusMessage = comments.statusMessage;
87
- const submitDisabled = composer.submitDisabled;
88
- const mentionItems = composer.mentionItems;
89
- const onOpenSavedComment = comments.onOpenSaved;
90
- const onRemoveSavedComment = comments.onRemoveSaved;
91
- const onCommentTextChange = composer.onTextChange;
92
- const onSubmitComment = composer.onSubmit;
93
- const textareaRef = useRef<HTMLTextAreaElement | null>(null);
94
- const composerRef = useRef<HTMLFormElement | null>(null);
95
- const rafRef = useRef<number | null>(null);
96
- const active = inspector.enabled && inspector.inspectorMode;
97
- const selectedTarget = inspector.selectedTarget;
98
- const selectedTargetKey = objectSelectionKey(selectedTarget);
99
- const activeSavedCommentForTarget = selectedTarget && activeSavedComment
100
- && inlineCommentTargetKey(activeSavedComment) === selectedTargetKey
101
- && activeSavedComment.placement === selectedTarget.placement
102
- ? activeSavedComment
103
- : null;
104
- const savedCommentForTarget = activeSavedCommentForTarget
105
- ?? getInlineSavedCommentForTarget(savedComments, selectedTarget);
106
- const markerEntries = useMemo<InlineSavedCommentMarkerEntry[]>(
107
- () => getInlineSavedCommentMarkers(savedComments),
108
- [savedComments],
109
- );
110
- const savedCommentLabels = useMemo(() => {
111
- const labels = new Map<string, string>();
112
- savedComments.forEach((comment, index) => labels.set(comment.id, String(index + 1)));
113
- return labels;
114
- }, [savedComments]);
115
- const markerEntriesByTarget = useMemo(
116
- () => new Set(markerEntries.map(({ target }) => objectSelectionKey(target))),
117
- [markerEntries],
118
- );
119
- const markerDisplayEntries = useMemo(
120
- () => [
121
- ...markerEntries,
122
- ...(selectedTarget && !markerEntriesByTarget.has(selectedTargetKey ?? "")
123
- ? [{ target: selectedTarget, comments: [] }]
124
- : []),
125
- ],
126
- [markerEntries, markerEntriesByTarget, selectedTarget, selectedTargetKey],
127
- );
128
- const [insertTargets, setInsertTargets] = useState<InspectorInsertTargetView[]>([]);
129
- const [selectionRect, setSelectionRect] = useState<InspectorLayerRect | null>(null);
130
- const [composerTargetKey, setComposerTargetKey] = useState<string | null>(null);
131
- const composerOpen = Boolean(selectedTargetKey && composerTargetKey === selectedTargetKey);
132
- const markerOnly = Boolean(savedCommentForTarget && !composerOpen && !activeSavedCommentForTarget);
133
- const {
134
- activeMention,
135
- handleMentionKeyDown,
136
- highlightedMentionIndex,
137
- mentionSuggestions,
138
- setHighlightedMentionIndex,
139
- setComposerCursor,
140
- syncCursor,
141
- insertMention,
142
- } = useComposerMentions({
143
- text: commentText,
144
- items: mentionItems,
145
- textareaRef,
146
- onTextChange: onCommentTextChange,
147
- enabled: composerOpen,
148
- });
149
-
150
- const updateLayer = useCallback(() => {
151
- const root = sourceContainerRef.current;
152
- if (!active || !root) {
153
- setInsertTargets([]);
154
- setSelectionRect(null);
155
- if (root) syncInspectorSelectedBlock(root, null);
156
- return;
157
- }
158
-
159
- const blockElements = collectInspectorBlockElements(root);
160
- const nextInsertTargets = createInspectorInsertTargets(blockElements);
161
- setInsertTargets(nextInsertTargets);
162
- setSelectionRect(resolveInspectorSelectionRect(root, selectedTarget, nextInsertTargets));
163
- syncInspectorSelectedBlock(root, markerOnly ? null : selectedTarget);
164
- }, [active, geometryVersion, markerOnly, selectedTarget, sourceContainerRef]);
165
-
166
- const scheduleLayerUpdate = useCallback(() => {
167
- if (rafRef.current !== null) window.cancelAnimationFrame(rafRef.current);
168
- rafRef.current = window.requestAnimationFrame(() => {
169
- rafRef.current = null;
170
- updateLayer();
171
- });
172
- }, [updateLayer]);
173
-
174
- useLayoutEffect(() => {
175
- updateLayer();
176
- }, [updateLayer]);
177
-
178
- useEffect(() => {
179
- if (!active) return undefined;
180
- const root = sourceContainerRef.current;
181
- const resizeObserver = typeof ResizeObserver === "undefined" || !root
182
- ? null
183
- : new ResizeObserver(scheduleLayerUpdate);
184
- if (root && resizeObserver) resizeObserver.observe(root);
185
- window.addEventListener("resize", scheduleLayerUpdate);
186
- window.addEventListener("scroll", scheduleLayerUpdate, true);
187
-
188
- return () => {
189
- resizeObserver?.disconnect();
190
- window.removeEventListener("resize", scheduleLayerUpdate);
191
- window.removeEventListener("scroll", scheduleLayerUpdate, true);
192
- if (rafRef.current !== null) window.cancelAnimationFrame(rafRef.current);
193
- rafRef.current = null;
194
- };
195
- }, [active, scheduleLayerUpdate, sourceContainerRef]);
196
-
197
- useEffect(() => {
198
- if (!selectedTarget || composerTargetKey !== selectedTargetKey) return undefined;
199
- let innerFrame: number | null = null;
200
- const outerFrame = window.requestAnimationFrame(() => {
201
- innerFrame = window.requestAnimationFrame(() => textareaRef.current?.focus({ preventScroll: true }));
202
- });
203
- return () => {
204
- window.cancelAnimationFrame(outerFrame);
205
- if (innerFrame !== null) window.cancelAnimationFrame(innerFrame);
206
- };
207
- }, [composerTargetKey, selectedTarget, selectedTargetKey]);
208
-
209
- useEffect(() => {
210
- setComposerTargetKey(null);
211
- }, [selectedTargetKey]);
212
-
213
- useEffect(() => {
214
- if (commentStatus === "saved") setComposerTargetKey(null);
215
- }, [commentStatus]);
216
-
217
- useEffect(() => {
218
- if (!composerOpen) return undefined;
219
-
220
- const isInsideComposer = (target: EventTarget | null) => {
221
- const composerElement = composerRef.current;
222
- return Boolean(composerElement && target instanceof Node && composerElement.contains(target));
223
- };
224
- const blockOutsideComposer = (event: Event) => {
225
- if (isInsideComposer(event.target)) return;
226
- event.preventDefault();
227
- event.stopPropagation();
228
- };
229
- const blockScrollKeyOutsideComposer = (event: KeyboardEvent) => {
230
- if (!isScrollKey(event) || isInsideComposer(event.target)) return;
231
- event.preventDefault();
232
- event.stopPropagation();
233
- };
234
-
235
- window.addEventListener("wheel", blockOutsideComposer, { capture: true, passive: false });
236
- window.addEventListener("touchmove", blockOutsideComposer, { capture: true, passive: false });
237
- window.addEventListener("keydown", blockScrollKeyOutsideComposer, { capture: true });
238
- return () => {
239
- window.removeEventListener("wheel", blockOutsideComposer, true);
240
- window.removeEventListener("touchmove", blockOutsideComposer, true);
241
- window.removeEventListener("keydown", blockScrollKeyOutsideComposer, true);
242
- };
243
- }, [composerOpen]);
244
-
245
- if (!active) return null;
246
-
247
- const composerStyle = selectionRect ? createInspectorComposerStyle(selectionRect, composerOpen) : undefined;
248
- const visibleActionItems = savedCommentForTarget
249
- ? COMPOSER_ACTIONS.filter((item) => item.action !== "add")
250
- : COMPOSER_ACTIONS;
251
- const applyComposerAction = (action: ComposerAction) => {
252
- if (!selectedTargetKey) return;
253
- const item = COMPOSER_ACTIONS.find((entry) => entry.action === action);
254
- if (!item) return;
255
- setComposerTargetKey(selectedTargetKey);
256
- if (!commentText.trim()) onCommentTextChange(item.prefix);
257
- };
258
- const handleMarkerClick = (target: ObjectSelection, comments: InlineSavedComment[]) => {
259
- if (!target) return;
260
- inspector.selectSelection(target);
261
- setComposerTargetKey(null);
262
- if (comments.length === 0) {
263
- onCommentTextChange("");
264
- return;
265
- }
266
- onOpenSavedComment(comments[0]!);
267
- };
268
- const getMarkerRect = (target: ObjectSelection) => {
269
- const root = sourceContainerRef.current;
270
- if (!root) return null;
271
- return resolveInspectorSelectionRect(root, target, insertTargets);
272
- };
273
- const markerViews = markerDisplayEntries.flatMap((markerEntry) => {
274
- const markerRect = getMarkerRect(markerEntry.target);
275
- if (!markerRect) return [];
276
- if (!isMarkerRectNearViewport(markerRect)) return [];
277
- return [{
278
- markerEntry,
279
- markerRect,
280
- markerLabel: markerLabelForEntry(markerEntry, savedCommentLabels, savedCommentTotalCount),
281
- markerStyle: createInspectorMarkerStyle(markerRect),
282
- }];
283
- }).sort((left, right) => compareMarkerRects(left.markerRect, right.markerRect));
284
-
285
- return (
286
- <div
287
- className="openpress-inline-inspector-layer"
288
- data-openpress-inline-inspector-layer
289
- data-openpress-composer-lock-events={composerOpen ? "true" : "false"}
290
- >
291
- {insertTargets.map((target) => {
292
- const isSelected = selectedTarget?.blockId === target.blockId && selectedTarget.placement === "before";
293
- return (
294
- <button
295
- type="button"
296
- className={`openpress-inline-insert-target${isSelected ? " is-selected" : ""}`}
297
- data-openpress-insert-before-block-id={target.blockId}
298
- style={rectToFixedStyle(target.rect)}
299
- aria-label="在此新增註解"
300
- key={target.blockId}
301
- onClick={() => inspector.selectSelection({ blockId: target.blockId, placement: "before" })}
302
- />
303
- );
304
- })}
305
-
306
- {markerViews.map(({ markerEntry, markerLabel, markerStyle }) => {
307
- const markerCount = markerEntry.comments.length;
308
- const hasSavedComment = markerEntry.comments.length > 0;
309
- return (
310
- <button
311
- type="button"
312
- className="openpress-inline-comment-marker"
313
- data-openpress-inline-comment-marker
314
- data-openpress-inline-comment-marker-object-id={markerEntry.target.objectId}
315
- data-openpress-inline-comment-marker-block-id={markerEntry.target.blockId}
316
- data-openpress-inline-comment-marker-placement={markerEntry.target.placement}
317
- data-openpress-marker-label={markerLabel}
318
- data-openpress-marker-state={hasSavedComment ? "saved" : "draft"}
319
- style={markerStyle}
320
- aria-label={hasSavedComment ? `編輯註解 ${markerLabel},${markerCount} 則` : `目前選取區塊 ${markerLabel}`}
321
- key={objectSelectionKey(markerEntry.target) ?? markerEntry.target.placement}
322
- onClick={() => handleMarkerClick(markerEntry.target, markerEntry.comments)}
323
- >
324
- <span className="openpress-inline-comment-marker__index">{markerLabel}</span>
325
- </button>
326
- );
327
- })}
328
-
329
- {selectionRect && selectedTarget && !markerOnly ? (
330
- <form
331
- ref={composerRef}
332
- className="openpress-inline-comment-composer"
333
- data-openpress-inline-comment-composer
334
- data-openpress-comment-placement={selectedTarget.placement}
335
- data-openpress-composer-open={composerOpen ? "true" : "false"}
336
- data-openpress-composer-saved={savedCommentForTarget ? "true" : "false"}
337
- style={composerStyle}
338
- onSubmit={(event) => void onSubmitComment(event)}
339
- >
340
- {!composerOpen ? (
341
- <div className="openpress-inline-comment-composer__intents" aria-label="註解意圖">
342
- {visibleActionItems.map((item) => {
343
- const Icon = item.icon;
344
- return (
345
- <button
346
- type="button"
347
- aria-label={item.label}
348
- title={item.label}
349
- key={item.action}
350
- onClick={() => {
351
- if (savedCommentForTarget && item.action === "delete") {
352
- void onRemoveSavedComment(savedCommentForTarget);
353
- return;
354
- }
355
- applyComposerAction(item.action);
356
- }}
357
- >
358
- <Icon aria-hidden="true" />
359
- </button>
360
- );
361
- })}
362
- </div>
363
- ) : null}
364
- {composerOpen ? (
365
- <div className="openpress-inline-comment-composer__body">
366
- <textarea
367
- ref={textareaRef}
368
- value={commentText}
369
- disabled={commentStatus === "submitting"}
370
- onChange={(event) => {
371
- onCommentTextChange(event.target.value);
372
- setComposerCursor(event.target.selectionStart ?? event.target.value.length);
373
- }}
374
- onClick={syncCursor}
375
- onKeyUp={syncCursor}
376
- onKeyDown={(event) => {
377
- if (handleMentionKeyDown(event)) return;
378
- if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
379
- event.preventDefault();
380
- void onSubmitComment();
381
- }
382
- }}
383
- aria-label={savedCommentForTarget ? "編輯註解" : "新增註解"}
384
- placeholder="新增註解..."
385
- rows={3}
386
- />
387
- <button type="submit" disabled={submitDisabled} aria-label="送出註解">
388
- <ArrowUp aria-hidden="true" />
389
- </button>
390
- </div>
391
- ) : null}
392
- {composerOpen ? (
393
- <MentionSuggestionList
394
- className="openpress-inline-comment-composer__suggestions"
395
- suggestions={mentionSuggestions}
396
- highlightedIndex={highlightedMentionIndex}
397
- ariaLabel={activeMention?.trigger === "/" ? "Skill suggestions" : "Mention suggestions"}
398
- onHighlight={setHighlightedMentionIndex}
399
- onSelect={insertMention}
400
- />
401
- ) : null}
402
- {composerOpen && commentStatusMessage ? (
403
- <p role="status" aria-live="polite" data-openpress-inspector-comment-status={commentStatus}>
404
- {commentStatusMessage}
405
- </p>
406
- ) : null}
407
- </form>
408
- ) : null}
409
- </div>
410
- );
411
- }
412
-
413
- function objectSelectionKey(target: ObjectSelection | null) {
414
- if (!target) return null;
415
- return `${target.objectId ?? target.blockId ?? "unknown"}:${target.placement}`;
416
- }
417
-
418
- function inlineCommentTargetKey(comment: InlineSavedComment) {
419
- return `${comment.objectId ?? comment.blockId ?? "unknown"}:${comment.placement}`;
420
- }
421
-
422
- function isScrollKey(event: KeyboardEvent) {
423
- return event.key === " "
424
- || event.key === "Spacebar"
425
- || event.key === "PageDown"
426
- || event.key === "PageUp"
427
- || event.key === "Home"
428
- || event.key === "End"
429
- || event.key === "ArrowDown"
430
- || event.key === "ArrowUp";
431
- }
432
-
433
- function compareMarkerRects(left: InspectorLayerRect, right: InspectorLayerRect) {
434
- const topDelta = left.top - right.top;
435
- if (Math.abs(topDelta) > 1) return topDelta;
436
- return left.left - right.left;
437
- }
438
-
439
- function markerLabelForEntry(
440
- markerEntry: InlineSavedCommentMarkerEntry,
441
- savedCommentLabels: Map<string, string>,
442
- savedCommentCount: number,
443
- ) {
444
- const firstComment = markerEntry.comments[0];
445
- if (!firstComment) return String(savedCommentCount + 1);
446
- if (firstComment.markerLabel) return firstComment.markerLabel;
447
- return savedCommentLabels.get(firstComment.id) ?? String(savedCommentCount + 1);
448
- }
449
-
450
- function isMarkerRectNearViewport(rect: InspectorLayerRect, margin = 48) {
451
- if (typeof window === "undefined") return true;
452
- return rect.top + rect.height >= -margin
453
- && rect.top <= window.innerHeight + margin
454
- && rect.left + rect.width >= -margin
455
- && rect.left <= window.innerWidth + margin;
456
- }
457
-
458
- export const InlineInspectorLayer = memo(InlineInspectorLayerImpl);
459
- InlineInspectorLayer.displayName = "InlineInspectorLayer";
@@ -1,5 +0,0 @@
1
- export * from "./InlineInspectorLayer";
2
- export * from "./inlineCommentModel";
3
- export * from "./inspectorGeometryModel";
4
- export * from "./inspectorModel";
5
- export * from "./useInspectorComments";
@@ -1,125 +0,0 @@
1
- import type { ObjectSelection, PendingComment } from "./inspectorModel";
2
- import type { SourceBlock } from "../../document-model";
3
- import { parseCommentHint } from "../workbenchFormatters";
4
- import type { InlineSavedComment, InlineSavedCommentMarkerEntry } from "../workbenchTypes";
5
-
6
- export function getInlineSavedCommentForTarget(
7
- comments: InlineSavedComment[],
8
- target: ObjectSelection | null,
9
- preferredId?: string | null,
10
- ) {
11
- if (!target) return null;
12
- const targetKey = objectSelectionKey(target);
13
- const targetComments = comments.filter((comment) => inlineCommentTargetKey(comment) === targetKey);
14
- if (!targetComments.length) return null;
15
- if (preferredId) {
16
- const preferred = targetComments.find((comment) => comment.id === preferredId);
17
- if (preferred) return preferred;
18
- }
19
- return targetComments[0] ?? null;
20
- }
21
-
22
- export function getInlineSavedCommentMarkers(comments: InlineSavedComment[]) {
23
- const markerMap = new Map<string, InlineSavedCommentMarkerEntry>();
24
-
25
- for (const comment of comments) {
26
- const target: ObjectSelection = {
27
- objectId: comment.objectId,
28
- blockId: comment.blockId,
29
- placement: comment.placement,
30
- };
31
- const key = objectSelectionKey(target);
32
- if (!key) continue;
33
- const bucket = markerMap.get(key);
34
- if (bucket) {
35
- bucket.comments.push(comment);
36
- } else {
37
- markerMap.set(key, { target, comments: [comment] });
38
- }
39
- }
40
-
41
- return Array.from(markerMap.values()).map((entry) => ({
42
- ...entry,
43
- comments: [...entry.comments],
44
- })).sort((left, right) => {
45
- const leftId = left.target.objectId ?? left.target.blockId ?? "";
46
- const rightId = right.target.objectId ?? right.target.blockId ?? "";
47
- if (leftId === rightId) {
48
- if (left.target.placement === right.target.placement) return 0;
49
- return left.target.placement === "before" ? -1 : 1;
50
- }
51
- return leftId.localeCompare(rightId, "zh-Hant");
52
- });
53
- }
54
-
55
- export function resolveInlineSavedComment(comment: PendingComment, sourceBlocksByPath: Record<string, SourceBlock[]>) {
56
- const target = resolveInlineSavedCommentTarget(comment, sourceBlocksByPath);
57
- if (!target) return [];
58
- const hintMeta = parseCommentHint(comment.hint);
59
- return [{
60
- id: comment.id,
61
- objectId: hintMeta?.targetObjectId,
62
- blockId: target.id,
63
- placement: hintMeta?.placement ?? "block",
64
- note: comment.note,
65
- path: comment.path,
66
- line: comment.line,
67
- timestamp: comment.timestamp,
68
- }];
69
- }
70
-
71
- function objectSelectionKey(target: ObjectSelection | null) {
72
- if (!target) return null;
73
- return `${target.objectId ?? target.blockId ?? "unknown"}\u0000${target.placement}`;
74
- }
75
-
76
- function inlineCommentTargetKey(comment: InlineSavedComment) {
77
- return `${comment.objectId ?? comment.blockId ?? "unknown"}\u0000${comment.placement}`;
78
- }
79
-
80
- export function groupSourceBlocksByPath(sourceBlockMap: Record<string, SourceBlock>) {
81
- const grouped = Object.values(sourceBlockMap).reduce<Record<string, SourceBlock[]>>((accumulator, sourceBlock) => {
82
- const path = normalizeSourcePath(sourceBlock.path);
83
- if (!path) return accumulator;
84
- if (!accumulator[path]) accumulator[path] = [];
85
- accumulator[path].push(sourceBlock);
86
- return accumulator;
87
- }, {});
88
-
89
- Object.values(grouped).forEach((blocks) => {
90
- blocks.sort((left, right) => {
91
- const leftLine = left.source?.line ?? Number.POSITIVE_INFINITY;
92
- const rightLine = right.source?.line ?? Number.POSITIVE_INFINITY;
93
- if (leftLine === rightLine) return left.id.localeCompare(right.id, "zh-Hant");
94
- return leftLine - rightLine;
95
- });
96
- });
97
-
98
- return grouped;
99
- }
100
-
101
- function resolveInlineSavedCommentTarget(comment: PendingComment, sourceBlocksByPath: Record<string, SourceBlock[]>) {
102
- if (!comment.path || !comment.line) return null;
103
- const commentPath = normalizeSourcePath(comment.path);
104
- const candidateBlocks = sourceBlocksByPath[commentPath];
105
- if (!candidateBlocks?.length) return null;
106
-
107
- const normalizedLine = Number(comment.line);
108
- if (!Number.isInteger(normalizedLine) || normalizedLine < 1) return null;
109
-
110
- let selectedBlock: SourceBlock | null = null;
111
- for (const block of candidateBlocks) {
112
- if (typeof block.source?.line !== "number") continue;
113
- if (block.source.line <= normalizedLine) {
114
- selectedBlock = block;
115
- continue;
116
- }
117
- break;
118
- }
119
- if (selectedBlock) return selectedBlock;
120
- return candidateBlocks[0] ?? null;
121
- }
122
-
123
- function normalizeSourcePath(value: string) {
124
- return value.trim().replaceAll("\\", "/").replace(/^\.\//, "").replace(/^document\//, "");
125
- }