@open-press/cli 1.0.0 → 1.1.1
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.
- package/README.md +11 -12
- package/dist/cli.js +298 -79
- package/package.json +9 -7
- package/template/core/AGENTS.md +0 -130
- package/template/core/CHANGELOG.md +0 -218
- package/template/core/README.md +0 -43
- package/template/core/engine/cli.mjs +0 -96
- package/template/core/engine/commands/_shared.mjs +0 -199
- package/template/core/engine/commands/deploy.mjs +0 -31
- package/template/core/engine/commands/dev.mjs +0 -49
- package/template/core/engine/commands/doctor.mjs +0 -229
- package/template/core/engine/commands/export.mjs +0 -8
- package/template/core/engine/commands/image.mjs +0 -29
- package/template/core/engine/commands/inspect.mjs +0 -35
- package/template/core/engine/commands/pdf.mjs +0 -26
- package/template/core/engine/commands/preview.mjs +0 -26
- package/template/core/engine/commands/render.mjs +0 -17
- package/template/core/engine/commands/replace.mjs +0 -41
- package/template/core/engine/commands/search.mjs +0 -33
- package/template/core/engine/commands/skills-sync.mjs +0 -71
- package/template/core/engine/commands/typecheck.mjs +0 -67
- package/template/core/engine/commands/upgrade.mjs +0 -159
- package/template/core/engine/commands/validate.mjs +0 -17
- package/template/core/engine/document-export.mjs +0 -15
- package/template/core/engine/output/chrome-pdf.d.mts +0 -34
- package/template/core/engine/output/chrome-pdf.mjs +0 -450
- package/template/core/engine/output/deploy-sync.mjs +0 -15
- package/template/core/engine/output/fonts.mjs +0 -62
- package/template/core/engine/output/katex-assets.mjs +0 -45
- package/template/core/engine/output/page-block.mjs +0 -30
- package/template/core/engine/output/pdf-media.mjs +0 -45
- package/template/core/engine/output/public-assets.mjs +0 -19
- package/template/core/engine/output/static-server.mjs +0 -571
- package/template/core/engine/react/caption-numbering.mjs +0 -73
- package/template/core/engine/react/comment-endpoint.d.mts +0 -11
- package/template/core/engine/react/comment-endpoint.mjs +0 -102
- package/template/core/engine/react/comment-marker.mjs +0 -374
- package/template/core/engine/react/document-entry.mjs +0 -331
- package/template/core/engine/react/document-export.mjs +0 -512
- package/template/core/engine/react/http-json.mjs +0 -24
- package/template/core/engine/react/mdx-compile.mjs +0 -629
- package/template/core/engine/react/measurement-css.mjs +0 -157
- package/template/core/engine/react/object-entities.mjs +0 -204
- package/template/core/engine/react/pagination/allocator.mjs +0 -167
- package/template/core/engine/react/pagination/regions.mjs +0 -81
- package/template/core/engine/react/pagination-constants.mjs +0 -3
- package/template/core/engine/react/pagination.mjs +0 -9
- package/template/core/engine/react/pipeline/allocate.mjs +0 -217
- package/template/core/engine/react/pipeline/final-render.mjs +0 -94
- package/template/core/engine/react/pipeline/frame-measurement.mjs +0 -306
- package/template/core/engine/react/pipeline/press-tree.mjs +0 -135
- package/template/core/engine/react/press-tree-inspection.mjs +0 -172
- package/template/core/engine/react/project-asset-endpoint.d.mts +0 -10
- package/template/core/engine/react/project-asset-endpoint.mjs +0 -361
- package/template/core/engine/react/section-css.mjs +0 -56
- package/template/core/engine/react/source-edit-endpoint.d.mts +0 -10
- package/template/core/engine/react/source-edit-endpoint.mjs +0 -75
- package/template/core/engine/react/sources/heading-numbering.mjs +0 -132
- package/template/core/engine/react/sources/mdx-resolver.mjs +0 -439
- package/template/core/engine/react/style-discovery.mjs +0 -160
- package/template/core/engine/runtime/config.d.mts +0 -48
- package/template/core/engine/runtime/config.mjs +0 -172
- package/template/core/engine/runtime/file-utils.mjs +0 -114
- package/template/core/engine/runtime/file-walk.mjs +0 -22
- package/template/core/engine/runtime/inspection.mjs +0 -328
- package/template/core/engine/runtime/issue-report.mjs +0 -44
- package/template/core/engine/runtime/page-geometry.mjs +0 -131
- package/template/core/engine/runtime/path-utils.mjs +0 -20
- package/template/core/engine/runtime/source-text-tools.d.mts +0 -102
- package/template/core/engine/runtime/source-text-tools.mjs +0 -832
- package/template/core/engine/runtime/source-workspace.mjs +0 -168
- package/template/core/engine/runtime/validation.mjs +0 -183
- package/template/core/index.html +0 -13
- package/template/core/openpress.config.mjs +0 -8
- package/template/core/package.json +0 -89
- package/template/core/src/main.tsx +0 -16
- package/template/core/src/openpress/app/OpenPressApp.tsx +0 -296
- package/template/core/src/openpress/app/OpenPressRuntime.tsx +0 -102
- package/template/core/src/openpress/app/WorkspaceGalleryPage.tsx +0 -219
- package/template/core/src/openpress/app/index.ts +0 -2
- package/template/core/src/openpress/core/Frame.tsx +0 -91
- package/template/core/src/openpress/core/FrameContext.tsx +0 -26
- package/template/core/src/openpress/core/MdxArea.tsx +0 -34
- package/template/core/src/openpress/core/Press.tsx +0 -55
- package/template/core/src/openpress/core/Workspace.tsx +0 -36
- package/template/core/src/openpress/core/cn.ts +0 -4
- package/template/core/src/openpress/core/index.tsx +0 -47
- package/template/core/src/openpress/core/primitives.tsx +0 -91
- package/template/core/src/openpress/core/types.ts +0 -236
- package/template/core/src/openpress/core/useSource.ts +0 -28
- package/template/core/src/openpress/document-model/anchorMapModel.ts +0 -27
- package/template/core/src/openpress/document-model/documentIndexes.ts +0 -329
- package/template/core/src/openpress/document-model/documentTypes.ts +0 -147
- package/template/core/src/openpress/document-model/index.ts +0 -7
- package/template/core/src/openpress/document-model/objectEntityModel.ts +0 -55
- package/template/core/src/openpress/document-model/projectIdentityModel.ts +0 -15
- package/template/core/src/openpress/document-model/reactDocumentMetadataModel.ts +0 -27
- package/template/core/src/openpress/document-model/workspaceManifestModel.ts +0 -57
- package/template/core/src/openpress/manuscript/index.tsx +0 -238
- package/template/core/src/openpress/mdx/index.ts +0 -96
- package/template/core/src/openpress/numbering/index.ts +0 -294
- package/template/core/src/openpress/reader/PageThumbnailsPanel.tsx +0 -168
- package/template/core/src/openpress/reader/PublicReaderPage.tsx +0 -267
- package/template/core/src/openpress/reader/ReaderNavigationPanel.tsx +0 -123
- package/template/core/src/openpress/reader/index.ts +0 -11
- package/template/core/src/openpress/reader/pageViewportScaleModel.ts +0 -73
- package/template/core/src/openpress/reader/readerPageRegistry.ts +0 -41
- package/template/core/src/openpress/reader/readerPageRoute.ts +0 -21
- package/template/core/src/openpress/reader/readerScroll.ts +0 -92
- package/template/core/src/openpress/reader/readerStateModel.ts +0 -15
- package/template/core/src/openpress/reader/readerTypes.ts +0 -4
- package/template/core/src/openpress/reader/usePageViewportScale.ts +0 -119
- package/template/core/src/openpress/reader/usePanelState.ts +0 -56
- package/template/core/src/openpress/reader/useReaderHashSync.ts +0 -61
- package/template/core/src/openpress/reader/useReaderKeyboardNav.ts +0 -48
- package/template/core/src/openpress/reader/useReaderRuntime.ts +0 -146
- package/template/core/src/openpress/reader/useReaderScrollAnchor.ts +0 -64
- package/template/core/src/openpress/shared/Panel.tsx +0 -77
- package/template/core/src/openpress/shared/frameScheduler.ts +0 -32
- package/template/core/src/openpress/shared/index.ts +0 -4
- package/template/core/src/openpress/shared/numberUtils.ts +0 -3
- package/template/core/src/openpress/shared/runtimeMode.ts +0 -11
- package/template/core/src/openpress/workbench/Workbench.tsx +0 -506
- package/template/core/src/openpress/workbench/actions/DeploymentControl.tsx +0 -157
- package/template/core/src/openpress/workbench/actions/ExportImageControl.tsx +0 -96
- package/template/core/src/openpress/workbench/actions/PageZoomControl.tsx +0 -182
- package/template/core/src/openpress/workbench/actions/SearchControl.tsx +0 -345
- package/template/core/src/openpress/workbench/actions/deploymentStatusModel.ts +0 -112
- package/template/core/src/openpress/workbench/actions/index.ts +0 -6
- package/template/core/src/openpress/workbench/actions/useDeploymentWorkbench.ts +0 -136
- package/template/core/src/openpress/workbench/dialog/WorkbenchDialog.tsx +0 -72
- package/template/core/src/openpress/workbench/dialog/index.ts +0 -1
- package/template/core/src/openpress/workbench/document/components/DocumentPanel.tsx +0 -127
- package/template/core/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +0 -207
- package/template/core/src/openpress/workbench/document/components/ReaderStage.tsx +0 -9
- package/template/core/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +0 -34
- package/template/core/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +0 -525
- package/template/core/src/openpress/workbench/document/index.ts +0 -10
- package/template/core/src/openpress/workbench/index.ts +0 -2
- package/template/core/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +0 -459
- package/template/core/src/openpress/workbench/inspector/index.ts +0 -5
- package/template/core/src/openpress/workbench/inspector/inlineCommentModel.ts +0 -125
- package/template/core/src/openpress/workbench/inspector/inspectorGeometryModel.ts +0 -160
- package/template/core/src/openpress/workbench/inspector/inspectorModel.ts +0 -408
- package/template/core/src/openpress/workbench/inspector/useInspectorComments.ts +0 -254
- package/template/core/src/openpress/workbench/mentions/MentionSuggestionList.tsx +0 -41
- package/template/core/src/openpress/workbench/mentions/index.ts +0 -2
- package/template/core/src/openpress/workbench/mentions/useComposerMentions.ts +0 -185
- package/template/core/src/openpress/workbench/panels/Panel.tsx +0 -1
- package/template/core/src/openpress/workbench/panels/PendingCommentsPanel.tsx +0 -80
- package/template/core/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +0 -29
- package/template/core/src/openpress/workbench/panels/index.ts +0 -3
- package/template/core/src/openpress/workbench/project/ProjectEntryPanel.tsx +0 -525
- package/template/core/src/openpress/workbench/project/ProjectPreviewDialog.tsx +0 -35
- package/template/core/src/openpress/workbench/project/index.ts +0 -2
- package/template/core/src/openpress/workbench/project/projectPreviewTypes.ts +0 -11
- package/template/core/src/openpress/workbench/project/projectSourceModel.ts +0 -24
- package/template/core/src/openpress/workbench/shell/WorkbenchShell.tsx +0 -167
- package/template/core/src/openpress/workbench/shell/index.ts +0 -1
- package/template/core/src/openpress/workbench/workbenchFormatters.ts +0 -120
- package/template/core/src/openpress/workbench/workbenchTypes.ts +0 -35
- package/template/core/src/styles/openpress/app-shell.css +0 -251
- package/template/core/src/styles/openpress/media-workspace.css +0 -230
- package/template/core/src/styles/openpress/print-route.css +0 -184
- package/template/core/src/styles/openpress/project-preview-panel.css +0 -924
- package/template/core/src/styles/openpress/public-viewer.css +0 -688
- package/template/core/src/styles/openpress/reader-runtime.css +0 -989
- package/template/core/src/styles/openpress/responsive.css +0 -245
- package/template/core/src/styles/openpress/workbench-panels.css +0 -707
- package/template/core/src/styles/openpress/workbench.css +0 -1255
- package/template/core/src/styles/openpress/workspace-gallery.css +0 -300
- package/template/core/src/styles/openpress.css +0 -15
- package/template/core/src/vite-env.d.ts +0 -9
- package/template/core/tsconfig.json +0 -40
- package/template/core/vite.config.ts +0 -584
|
@@ -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,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
|
-
}
|