@open-press/core 0.7.1 → 0.8.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.
- package/engine/commands/dev.mjs +2 -2
- package/engine/output/chrome-pdf.mjs +18 -3
- package/engine/output/static-server.mjs +39 -0
- package/engine/react/comment-endpoint.mjs +13 -39
- package/engine/react/comment-marker.mjs +30 -6
- package/engine/react/document-entry.mjs +11 -0
- package/engine/react/document-export.mjs +30 -5
- package/engine/react/http-json.mjs +24 -0
- package/engine/react/mdx-compile.mjs +96 -3
- package/engine/react/measurement-css.mjs +93 -1
- package/engine/react/object-entities.mjs +119 -0
- package/engine/react/pipeline/allocate.mjs +10 -7
- package/engine/react/pipeline/frame-measurement.mjs +2 -0
- package/engine/react/project-asset-endpoint.mjs +6 -24
- package/engine/react/source-edit-endpoint.d.mts +10 -0
- package/engine/react/source-edit-endpoint.mjs +75 -0
- package/engine/react/sources/mdx-resolver.mjs +12 -14
- package/engine/react/style-discovery.mjs +1 -4
- package/engine/runtime/file-walk.mjs +22 -0
- package/engine/runtime/inspection.mjs +1 -20
- package/engine/runtime/path-utils.mjs +20 -0
- package/engine/runtime/source-text-tools.d.mts +102 -0
- package/engine/runtime/source-text-tools.mjs +551 -16
- package/engine/runtime/source-workspace.mjs +4 -31
- package/package.json +1 -1
- package/src/openpress/{App.tsx → app/OpenPressApp.tsx} +25 -12
- package/src/openpress/{renderer.tsx → app/OpenPressRuntime.tsx} +10 -7
- package/src/openpress/app/index.ts +2 -0
- package/src/openpress/core/Frame.tsx +9 -11
- package/src/openpress/core/FrameContext.tsx +8 -3
- package/src/openpress/core/MdxArea.tsx +11 -12
- package/src/openpress/core/cn.ts +4 -0
- package/src/openpress/core/index.tsx +2 -1
- package/src/openpress/core/primitives.tsx +29 -8
- package/src/openpress/core/types.ts +8 -0
- package/src/openpress/{anchorMap.ts → document-model/anchorMapModel.ts} +1 -1
- package/src/openpress/{indexes.ts → document-model/documentIndexes.ts} +1 -1
- package/src/openpress/{types.ts → document-model/documentTypes.ts} +42 -0
- package/src/openpress/document-model/index.ts +6 -0
- package/src/openpress/document-model/objectEntityModel.ts +51 -0
- package/src/openpress/{projectIdentity.ts → document-model/projectIdentityModel.ts} +1 -1
- package/src/openpress/{reactDocumentMetadata.ts → document-model/reactDocumentMetadataModel.ts} +1 -1
- package/src/openpress/manuscript/index.tsx +49 -7
- package/src/openpress/{publicPage.tsx → reader/PublicReaderPage.tsx} +31 -51
- package/src/openpress/{workbenchPanels.tsx → reader/ReaderNavigationPanel.tsx} +6 -5
- package/src/openpress/reader/index.ts +10 -0
- package/src/openpress/reader/pageViewportScaleModel.ts +73 -0
- package/src/openpress/reader/readerTypes.ts +4 -0
- package/src/openpress/reader/usePageViewportScale.ts +119 -0
- package/src/openpress/reader/usePanelState.ts +56 -0
- package/src/openpress/reader/useReaderHashSync.ts +61 -0
- package/src/openpress/reader/useReaderKeyboardNav.ts +48 -0
- package/src/openpress/reader/useReaderRuntime.ts +146 -0
- package/src/openpress/reader/useReaderScrollAnchor.ts +64 -0
- package/src/openpress/shared/Panel.tsx +77 -0
- package/src/openpress/shared/index.ts +4 -0
- package/src/openpress/shared/numberUtils.ts +3 -0
- package/src/openpress/{runtimeMode.ts → shared/runtimeMode.ts} +0 -11
- package/src/openpress/workbench/Workbench.tsx +407 -0
- package/src/openpress/workbench/actions/DeploymentControl.tsx +157 -0
- package/src/openpress/workbench/actions/PageZoomControl.tsx +182 -0
- package/src/openpress/workbench/actions/SearchControl.tsx +345 -0
- package/src/openpress/workbench/actions/deploymentStatusModel.ts +112 -0
- package/src/openpress/workbench/actions/index.ts +5 -0
- package/src/openpress/workbench/actions/useDeploymentWorkbench.ts +136 -0
- package/src/openpress/workbench/dialog/WorkbenchDialog.tsx +72 -0
- package/src/openpress/workbench/dialog/index.ts +1 -0
- package/src/openpress/workbench/document/components/DocumentPanel.tsx +127 -0
- package/src/openpress/workbench/document/components/InlineSourceEditorLayer.tsx +207 -0
- package/src/openpress/workbench/document/components/ReaderStage.tsx +9 -0
- package/src/openpress/workbench/document/hooks/useDocumentWorkbenchModel.ts +34 -0
- package/src/openpress/workbench/document/hooks/useInlineDocumentEditor.ts +525 -0
- package/src/openpress/workbench/document/index.ts +10 -0
- package/src/openpress/workbench/index.ts +2 -0
- package/src/openpress/workbench/inspector/InlineInspectorLayer.tsx +459 -0
- package/src/openpress/workbench/inspector/index.ts +5 -0
- package/src/openpress/workbench/inspector/inlineCommentModel.ts +125 -0
- package/src/openpress/workbench/inspector/inspectorGeometryModel.ts +160 -0
- package/src/openpress/workbench/inspector/inspectorModel.ts +408 -0
- package/src/openpress/workbench/inspector/useInspectorComments.ts +248 -0
- package/src/openpress/workbench/mentions/MentionSuggestionList.tsx +41 -0
- package/src/openpress/workbench/mentions/index.ts +2 -0
- package/src/openpress/{composerMentions.ts → workbench/mentions/useComposerMentions.ts} +1 -4
- package/src/openpress/workbench/panels/Panel.tsx +1 -0
- package/src/openpress/workbench/panels/PendingCommentsPanel.tsx +76 -0
- package/src/openpress/workbench/panels/WorkbenchControlPanel.tsx +29 -0
- package/src/openpress/workbench/panels/index.ts +3 -0
- package/src/openpress/workbench/project/ProjectEntryPanel.tsx +523 -0
- package/src/openpress/workbench/project/ProjectPreviewDialog.tsx +35 -0
- package/src/openpress/workbench/project/index.ts +2 -0
- package/src/openpress/workbench/project/projectPreviewTypes.ts +11 -0
- package/src/openpress/workbench/shell/WorkbenchShell.tsx +167 -0
- package/src/openpress/workbench/shell/index.ts +1 -0
- package/src/openpress/workbench/workbenchFormatters.ts +120 -0
- package/src/openpress/workbench/workbenchTypes.ts +35 -0
- package/src/styles/openpress/print-route.css +0 -2
- package/src/styles/openpress/{project-workspace.css → project-preview-panel.css} +13 -407
- package/src/styles/openpress/public-viewer.css +25 -320
- package/src/styles/openpress/reader-runtime.css +243 -55
- package/src/styles/openpress/responsive.css +145 -270
- package/src/styles/openpress/workbench-panels.css +214 -178
- package/src/styles/openpress/workbench.css +986 -451
- package/src/styles/openpress.css +1 -1
- package/vite.config.ts +50 -0
- package/src/openpress/inspector.ts +0 -282
- package/src/openpress/projectWorkspace.tsx +0 -919
- package/src/openpress/readerRuntime.ts +0 -230
- package/src/openpress/workbench.tsx +0 -1265
- package/src/openpress/workbenchTypes.ts +0 -4
- /package/src/openpress/{readerPageRegistry.ts → reader/readerPageRegistry.ts} +0 -0
- /package/src/openpress/{pageRoute.ts → reader/readerPageRoute.ts} +0 -0
- /package/src/openpress/{readerScroll.ts → reader/readerScroll.ts} +0 -0
- /package/src/openpress/{readerState.ts → reader/readerStateModel.ts} +0 -0
- /package/src/openpress/{frameScheduler.ts → shared/frameScheduler.ts} +0 -0
- /package/src/openpress/{projectSources.ts → workbench/project/projectSourceModel.ts} +0 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { createContext, useContext, type CSSProperties, type ReactNode } from "react";
|
|
2
|
+
import { PanelLeftClose, PanelLeftOpen, PanelRightClose, PanelRightOpen, X } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
type WorkbenchShellContextValue = {
|
|
5
|
+
leftPanelOpen: boolean;
|
|
6
|
+
rightPanelOpen: boolean;
|
|
7
|
+
onToggleLeftPanel: () => void;
|
|
8
|
+
onToggleRightPanel: () => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const WorkbenchShellContext = createContext<WorkbenchShellContextValue | null>(null);
|
|
12
|
+
|
|
13
|
+
function useWorkbenchShell() {
|
|
14
|
+
const value = useContext(WorkbenchShellContext);
|
|
15
|
+
if (!value) throw new Error("WorkbenchShell compound components must be rendered inside <WorkbenchShell>.");
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function WorkbenchShellRoot({
|
|
20
|
+
style,
|
|
21
|
+
devMode,
|
|
22
|
+
viewMode,
|
|
23
|
+
inspectorMode,
|
|
24
|
+
editMode = false,
|
|
25
|
+
leftPanelOpen,
|
|
26
|
+
rightPanelOpen,
|
|
27
|
+
onToggleLeftPanel,
|
|
28
|
+
onToggleRightPanel,
|
|
29
|
+
children,
|
|
30
|
+
}: {
|
|
31
|
+
style: CSSProperties;
|
|
32
|
+
devMode: boolean;
|
|
33
|
+
viewMode: string;
|
|
34
|
+
inspectorMode: boolean;
|
|
35
|
+
editMode?: boolean;
|
|
36
|
+
leftPanelOpen: boolean;
|
|
37
|
+
rightPanelOpen: boolean;
|
|
38
|
+
onToggleLeftPanel: () => void;
|
|
39
|
+
onToggleRightPanel: () => void;
|
|
40
|
+
children: ReactNode;
|
|
41
|
+
}) {
|
|
42
|
+
const scrimOpen = leftPanelOpen || rightPanelOpen;
|
|
43
|
+
const handleScrimClick = rightPanelOpen ? onToggleRightPanel : onToggleLeftPanel;
|
|
44
|
+
const shellClassName = [
|
|
45
|
+
"reader-app openpress-reader-app openpress-public-viewer openpress-dev-public-viewer openpress-workbench-shell is-ready",
|
|
46
|
+
leftPanelOpen ? "" : "is-closed-left",
|
|
47
|
+
rightPanelOpen ? "" : "is-closed-right",
|
|
48
|
+
].filter(Boolean).join(" ");
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<WorkbenchShellContext.Provider value={{ leftPanelOpen, rightPanelOpen, onToggleLeftPanel, onToggleRightPanel }}>
|
|
52
|
+
<main className="openpress-workbench" style={style} data-dev-mode={devMode ? "true" : "false"}>
|
|
53
|
+
<div
|
|
54
|
+
className={shellClassName}
|
|
55
|
+
data-openpress-react-runtime="true"
|
|
56
|
+
data-openpress-view-mode={viewMode}
|
|
57
|
+
data-openpress-inspector-mode={inspectorMode ? "on" : "off"}
|
|
58
|
+
data-openpress-edit-mode={editMode ? "on" : "off"}
|
|
59
|
+
data-openpress-workbench-shell
|
|
60
|
+
data-testid="workbench-shell"
|
|
61
|
+
>
|
|
62
|
+
{scrimOpen ? (
|
|
63
|
+
<div className="openpress-public-scrim" aria-hidden="true" onClick={handleScrimClick} />
|
|
64
|
+
) : null}
|
|
65
|
+
{children}
|
|
66
|
+
</div>
|
|
67
|
+
</main>
|
|
68
|
+
</WorkbenchShellContext.Provider>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function WorkbenchToolbar({ children }: { children: ReactNode }) {
|
|
73
|
+
const {
|
|
74
|
+
leftPanelOpen,
|
|
75
|
+
rightPanelOpen,
|
|
76
|
+
onToggleLeftPanel,
|
|
77
|
+
onToggleRightPanel,
|
|
78
|
+
} = useWorkbenchShell();
|
|
79
|
+
const LeftIcon = leftPanelOpen ? PanelLeftClose : PanelLeftOpen;
|
|
80
|
+
const RightIcon = rightPanelOpen ? PanelRightClose : PanelRightOpen;
|
|
81
|
+
const leftLabel = leftPanelOpen ? "收合左側面板" : "展開左側面板";
|
|
82
|
+
const rightLabel = rightPanelOpen ? "收合右側面板" : "展開右側面板";
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<header
|
|
86
|
+
className="openpress-workbench-toolbar"
|
|
87
|
+
role="toolbar"
|
|
88
|
+
aria-label="工作台操作"
|
|
89
|
+
data-openpress-workbench-toolbar
|
|
90
|
+
>
|
|
91
|
+
<button
|
|
92
|
+
type="button"
|
|
93
|
+
className="openpress-workbench-toolbar-panel-toggle"
|
|
94
|
+
data-openpress-toggle-left-panel
|
|
95
|
+
data-openpress-panel-open={leftPanelOpen ? "true" : "false"}
|
|
96
|
+
aria-label={leftLabel}
|
|
97
|
+
title={leftLabel}
|
|
98
|
+
onClick={onToggleLeftPanel}
|
|
99
|
+
>
|
|
100
|
+
<LeftIcon aria-hidden="true" />
|
|
101
|
+
</button>
|
|
102
|
+
<div className="openpress-workbench-toolbar__content">
|
|
103
|
+
{children}
|
|
104
|
+
</div>
|
|
105
|
+
<button
|
|
106
|
+
type="button"
|
|
107
|
+
className="openpress-workbench-toolbar-panel-toggle"
|
|
108
|
+
data-openpress-toggle-right-panel
|
|
109
|
+
data-openpress-panel-open={rightPanelOpen ? "true" : "false"}
|
|
110
|
+
aria-label={rightLabel}
|
|
111
|
+
title={rightLabel}
|
|
112
|
+
onClick={onToggleRightPanel}
|
|
113
|
+
>
|
|
114
|
+
<RightIcon aria-hidden="true" />
|
|
115
|
+
</button>
|
|
116
|
+
</header>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function WorkbenchLeftPanel({ children }: { children: ReactNode }) {
|
|
121
|
+
return (
|
|
122
|
+
<aside
|
|
123
|
+
className="reader-side-nav openpress-workspace-panel openpress-workbench-left-panel openpress-public-navigation"
|
|
124
|
+
aria-label="文件導覽"
|
|
125
|
+
data-openpress-left-panel
|
|
126
|
+
>
|
|
127
|
+
{children}
|
|
128
|
+
</aside>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function WorkbenchRightPanel({ children }: { children: ReactNode }) {
|
|
133
|
+
const { onToggleRightPanel } = useWorkbenchShell();
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<aside
|
|
137
|
+
className="openpress-workspace-panel openpress-workbench-right-panel openpress-dev-public-navigation"
|
|
138
|
+
aria-label="控制面板"
|
|
139
|
+
data-openpress-right-panel
|
|
140
|
+
>
|
|
141
|
+
<button type="button" className="openpress-public-drawer-close" aria-label="關閉右側面板" onClick={onToggleRightPanel}>
|
|
142
|
+
<X size={16} aria-hidden="true" />
|
|
143
|
+
</button>
|
|
144
|
+
{children}
|
|
145
|
+
</aside>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function WorkbenchMainContent({ children }: { children: ReactNode }) {
|
|
150
|
+
return (
|
|
151
|
+
<section
|
|
152
|
+
className="openpress-workbench__stage openpress-workbench-main openpress-public-viewer__stage openpress-dev-main-content"
|
|
153
|
+
aria-label="主要內容"
|
|
154
|
+
data-openpress-main-content
|
|
155
|
+
>
|
|
156
|
+
{children}
|
|
157
|
+
</section>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const WorkbenchShell = Object.assign(WorkbenchShellRoot, {
|
|
162
|
+
Toolbar: WorkbenchToolbar,
|
|
163
|
+
LeftPanel: WorkbenchLeftPanel,
|
|
164
|
+
RightPanel: WorkbenchRightPanel,
|
|
165
|
+
ControlPanel: WorkbenchRightPanel,
|
|
166
|
+
MainContent: WorkbenchMainContent,
|
|
167
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./WorkbenchShell";
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { InspectorPlacement } from "./inspector";
|
|
2
|
+
import type { ObjectEntity, SourceBlock, Theme } from "../document-model";
|
|
3
|
+
import type { PendingCommentsStatus, InspectorCommentStatus } from "./workbenchTypes";
|
|
4
|
+
|
|
5
|
+
export type PageGeometrySpec = {
|
|
6
|
+
label: string;
|
|
7
|
+
dimensions: string;
|
|
8
|
+
title: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const DEFAULT_PAGE_GEOMETRY = {
|
|
12
|
+
pageWidth: "210mm",
|
|
13
|
+
pageHeight: "297mm",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function formatInspectorSelection(block: SourceBlock | null, entity?: ObjectEntity | null) {
|
|
17
|
+
if (entity) return entity.label;
|
|
18
|
+
if (!block) return "未選取";
|
|
19
|
+
const line = block.source?.line;
|
|
20
|
+
return line ? `${block.path}:${line}` : block.path;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function formatInspectorCommentStatus(status: InspectorCommentStatus, error: string) {
|
|
24
|
+
if (status === "submitting") return "寫入中";
|
|
25
|
+
if (status === "saved") return "已寫入 source";
|
|
26
|
+
if (status === "failed") return error || "寫入失敗";
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function formatCommentsCount(count: number, status: PendingCommentsStatus) {
|
|
31
|
+
if (status === "loading") return "正在讀取";
|
|
32
|
+
if (status === "clearing") return "正在清除";
|
|
33
|
+
return `${count} 則待處理`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function formatPageGeometrySpec(theme?: Pick<Theme, "pageWidth" | "pageHeight">): PageGeometrySpec {
|
|
37
|
+
const width = parseCssLength(theme?.pageWidth ?? DEFAULT_PAGE_GEOMETRY.pageWidth);
|
|
38
|
+
const height = parseCssLength(theme?.pageHeight ?? DEFAULT_PAGE_GEOMETRY.pageHeight);
|
|
39
|
+
const dimensions = formatLengthPair(width, height);
|
|
40
|
+
const label = pageGeometryLabel(width, height);
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
label,
|
|
44
|
+
dimensions,
|
|
45
|
+
title: `${label} · ${dimensions}`,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function parseCommentHint(hint?: string) {
|
|
50
|
+
if (!hint?.startsWith("openpress-react-inspector")) return null;
|
|
51
|
+
const intent = hint.match(/\bintent=(add|edit|delete)\b/)?.[1];
|
|
52
|
+
const placement = hint.match(/\bplacement=(block|before)\b/)?.[1] as InspectorPlacement | undefined;
|
|
53
|
+
const targetObjectId = decodeHintValue(hint.match(/\btarget=([^\s]+)/)?.[1]);
|
|
54
|
+
const intentLabel = intent === "add" ? "Add" : intent === "delete" ? "Remove" : "Edit";
|
|
55
|
+
const placementLabel = placement === "before" ? "插入於區塊前" : "針對目前區塊";
|
|
56
|
+
return { intent: intent ?? "edit", intentLabel, placement: placement ?? "block", placementLabel, targetObjectId };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function formatCommentTimestamp(value: string) {
|
|
60
|
+
const date = new Date(value);
|
|
61
|
+
if (Number.isNaN(date.getTime())) return value;
|
|
62
|
+
return new Intl.DateTimeFormat("zh-TW", {
|
|
63
|
+
month: "2-digit",
|
|
64
|
+
day: "2-digit",
|
|
65
|
+
hour: "2-digit",
|
|
66
|
+
minute: "2-digit",
|
|
67
|
+
hour12: false,
|
|
68
|
+
}).format(date);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function pageGeometryLabel(width: CssLength, height: CssLength) {
|
|
72
|
+
if (matchesPageSize(width, height, "210", "297", "mm")) return "A4 Page";
|
|
73
|
+
if (matchesPageSize(width, height, "176", "250", "mm")) return "B5 Page";
|
|
74
|
+
if (matchesPageSize(width, height, "215.9", "279.4", "mm")) return "Letter Page";
|
|
75
|
+
if (isSixteenByNine(width, height)) return "16:9 Page";
|
|
76
|
+
return "Custom Page";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type CssLength = {
|
|
80
|
+
raw: string;
|
|
81
|
+
value: string | null;
|
|
82
|
+
unit: string | null;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function parseCssLength(value: string): CssLength {
|
|
86
|
+
const raw = value.trim();
|
|
87
|
+
const match = raw.match(/^(-?\d+(?:\.\d+)?)([a-z%]+)$/i);
|
|
88
|
+
if (!match) return { raw, value: null, unit: null };
|
|
89
|
+
return { raw, value: trimTrailingZeroes(match[1]), unit: match[2] };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function formatLengthPair(width: CssLength, height: CssLength) {
|
|
93
|
+
if (width.value && height.value && width.unit && width.unit === height.unit) {
|
|
94
|
+
return `${width.value} × ${height.value} ${width.unit}`;
|
|
95
|
+
}
|
|
96
|
+
return `${width.raw} × ${height.raw}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function matchesPageSize(width: CssLength, height: CssLength, targetWidth: string, targetHeight: string, unit: string) {
|
|
100
|
+
return width.value === targetWidth && height.value === targetHeight && width.unit === unit && height.unit === unit;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isSixteenByNine(width: CssLength, height: CssLength) {
|
|
104
|
+
if (!width.value || !height.value || !width.unit || width.unit !== height.unit) return false;
|
|
105
|
+
const ratio = Number(width.value) / Number(height.value);
|
|
106
|
+
return ratio > 1 && Math.abs(ratio - (16 / 9)) < 0.02;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function decodeHintValue(value?: string) {
|
|
110
|
+
if (!value) return undefined;
|
|
111
|
+
try {
|
|
112
|
+
return decodeURIComponent(value);
|
|
113
|
+
} catch {
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function trimTrailingZeroes(value: string) {
|
|
119
|
+
return value.replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
|
|
120
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { InspectorPlacement, ObjectSelection } from "./inspector/inspectorModel";
|
|
2
|
+
|
|
3
|
+
export type DeployStatus = "idle" | "deploying" | "deployed" | "unavailable" | "failed" | "setup";
|
|
4
|
+
export type PdfActionStatus = "idle" | "generating" | "opening" | "failed";
|
|
5
|
+
export type InspectorCommentStatus = "idle" | "submitting" | "saved" | "failed";
|
|
6
|
+
export type PendingCommentsStatus = "idle" | "loading" | "ready" | "failed" | "clearing";
|
|
7
|
+
|
|
8
|
+
export interface InlineSavedComment {
|
|
9
|
+
id: string;
|
|
10
|
+
objectId?: string;
|
|
11
|
+
blockId?: string;
|
|
12
|
+
placement: InspectorPlacement;
|
|
13
|
+
note: string;
|
|
14
|
+
path?: string;
|
|
15
|
+
line?: number;
|
|
16
|
+
timestamp?: string;
|
|
17
|
+
markerLabel?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface InlineSavedCommentMarkerEntry {
|
|
21
|
+
target: ObjectSelection;
|
|
22
|
+
comments: InlineSavedComment[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface InspectorLayerRect {
|
|
26
|
+
top: number;
|
|
27
|
+
left: number;
|
|
28
|
+
width: number;
|
|
29
|
+
height: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface InspectorInsertTargetView {
|
|
33
|
+
blockId: string;
|
|
34
|
+
rect: InspectorLayerRect;
|
|
35
|
+
}
|