@pretextbook/web-editor 0.4.3 → 0.5.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/dist/BareHostHarness.d.ts +2 -0
- package/dist/components/toc/SectionEditForm.d.ts +3 -1
- package/dist/components/toc/SectionItem.d.ts +2 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +1412 -1288
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +66 -66
- package/dist/index.js.map +1 -1
- package/dist/sectionUtils.d.ts +55 -4
- package/dist/store/editorStore.d.ts +53 -12
- package/package.json +1 -1
package/dist/sectionUtils.d.ts
CHANGED
|
@@ -97,6 +97,13 @@ export declare function ensureLatexSectionWrapper(content: string, type: Documen
|
|
|
97
97
|
* - For `\begin{section}` style: updates the `\title{…}` inside.
|
|
98
98
|
*/
|
|
99
99
|
export declare function updateLatexSectionTitle(content: string, newTitle: string): string;
|
|
100
|
+
/**
|
|
101
|
+
* Derive a LaTeX section's title directly from its header — the code
|
|
102
|
+
* editor's source-of-truth content — mirroring the two header styles
|
|
103
|
+
* {@link updateLatexSectionTitle} writes. Returns `null` when no header is
|
|
104
|
+
* found (introduction/conclusion have none), so callers leave title as-is.
|
|
105
|
+
*/
|
|
106
|
+
export declare function extractLatexDivisionTitle(content: string): string | null;
|
|
100
107
|
/** Create a new blank LaTeX section as a `Division`. */
|
|
101
108
|
export declare function createNewLatexSection(title?: string): DocumentSection;
|
|
102
109
|
/** Create a blank LaTeX introduction. */
|
|
@@ -123,6 +130,22 @@ export declare function getSectionAttributes(content: string): {
|
|
|
123
130
|
xmlId: string;
|
|
124
131
|
label: string;
|
|
125
132
|
};
|
|
133
|
+
/**
|
|
134
|
+
* Derive a division's title, type, `xml:id`, and `label` directly from its
|
|
135
|
+
* full PreTeXt source — the code editor's content, wrapper tag included.
|
|
136
|
+
* Used to keep the TOC in sync when the user edits these directly in the
|
|
137
|
+
* source rather than through the metadata dropdown form.
|
|
138
|
+
*
|
|
139
|
+
* Returns `null` when `content` isn't well-formed XML or its root element
|
|
140
|
+
* isn't a recognised division tag (both common mid-edit), so callers can
|
|
141
|
+
* skip the update rather than clobbering existing metadata with junk.
|
|
142
|
+
*/
|
|
143
|
+
export declare function extractDivisionMetadata(content: string): {
|
|
144
|
+
title: string;
|
|
145
|
+
type: DivisionType;
|
|
146
|
+
xmlId: string;
|
|
147
|
+
label: string;
|
|
148
|
+
} | null;
|
|
126
149
|
/**
|
|
127
150
|
* Update the title, tag name (type), `xml:id`, and `label` of a section.
|
|
128
151
|
*
|
|
@@ -201,6 +224,26 @@ export declare function removeDivisionRef(content: string, xmlId: string): strin
|
|
|
201
224
|
* - `afterXmlId` moves it immediately after that ref.
|
|
202
225
|
*/
|
|
203
226
|
export declare function moveDivisionRef(content: string, xmlId: string, afterXmlId: string | null): string;
|
|
227
|
+
/**
|
|
228
|
+
* Rename an existing `<plus:* ref="oldXmlId"/>` placeholder in-place to point
|
|
229
|
+
* at `newXmlId`, also updating the `*` tag name to `newType` if it changed.
|
|
230
|
+
* Unlike {@link moveDivisionRef}, the placeholder's position is left
|
|
231
|
+
* untouched — only its `ref` value and element name are rewritten.
|
|
232
|
+
*
|
|
233
|
+
* Used to keep a parent division's child placeholder in sync when the
|
|
234
|
+
* child's own `xml:id`/type are edited directly in its source, so the
|
|
235
|
+
* rename doesn't orphan the child from its parent.
|
|
236
|
+
*
|
|
237
|
+
* Returns `content` unchanged if no placeholder for `oldXmlId` is found.
|
|
238
|
+
*/
|
|
239
|
+
export declare function renameDivisionRef(content: string, oldXmlId: string, newXmlId: string, newType: DivisionType): string;
|
|
240
|
+
/**
|
|
241
|
+
* Find the division in `divisions` whose content contains a
|
|
242
|
+
* `<plus:* ref="xmlId"/>` placeholder for `xmlId` — i.e. `xmlId`'s parent in
|
|
243
|
+
* the division tree. Returns `null` if `xmlId` is unplaced (orphaned) or is
|
|
244
|
+
* the root.
|
|
245
|
+
*/
|
|
246
|
+
export declare function findDivisionParent(divisions: Division[], xmlId: string): Division | null;
|
|
204
247
|
/**
|
|
205
248
|
* Rewrite `content` so its `<plus:* ref="..."/>` placeholders appear in the
|
|
206
249
|
* order given by `orderedXmlIds`.
|
|
@@ -270,15 +313,23 @@ export declare function assembleProjectSource(divisions: Division[], rootXmlId:
|
|
|
270
313
|
* `<section xml:id="...">...</section>`) into a standalone PreTeXt fragment
|
|
271
314
|
* document suitable for a build-server preview of just that division.
|
|
272
315
|
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
316
|
+
* This function itself never expands `<plus:* ref="..."/>` placeholders —
|
|
317
|
+
* the real build server has no notion of that placeholder syntax, so callers
|
|
318
|
+
* must resolve them first (e.g. via {@link assembleProjectSource}) before
|
|
319
|
+
* passing `divisionXml` in. Passing unresolved refs produces invalid PreTeXt
|
|
320
|
+
* and a build failure.
|
|
276
321
|
*
|
|
277
322
|
* `divisionType` determines the minimal wrapper needed around `divisionXml`:
|
|
278
323
|
* root types (`book`/`article`/`slideshow`) need none, `chapter`/`part` are
|
|
279
324
|
* wrapped in a bare `<book>`, and everything else in a bare `<article>`.
|
|
325
|
+
* The PreTeXt schema requires `<book>`/`<article>` to have a `<title>` as
|
|
326
|
+
* their first child, so a wrapper built here uses `wrapperTitle` for that —
|
|
327
|
+
* without it the build server's schema validation rejects the document,
|
|
328
|
+
* produces no output, and 500s.
|
|
280
329
|
* `docinfo` (the full `<docinfo>...</docinfo>` element, or `""`) is inserted
|
|
281
330
|
* as a sibling of the root element inside `<pretext>`, matching real PreTeXt
|
|
282
331
|
* document shape.
|
|
332
|
+
*
|
|
333
|
+
* We need to add `label="preview"` (or really any label) since if this isn't present the build server won't know what file to return.
|
|
283
334
|
*/
|
|
284
|
-
export declare function wrapDivisionForPreview(divisionType: DivisionType, divisionXml: string, docinfo: string): string;
|
|
335
|
+
export declare function wrapDivisionForPreview(divisionType: DivisionType, divisionXml: string, docinfo: string, wrapperTitle: string): string;
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-instance Zustand store for the Editors component.
|
|
3
3
|
*
|
|
4
|
-
* ARCHITECTURE NOTE —
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* the
|
|
4
|
+
* ARCHITECTURE NOTE — the store owns the live editing buffer:
|
|
5
|
+
* `createEditorStore(init)` seeds the editing buffer (`divisions`, `title`,
|
|
6
|
+
* `docinfo`, `activeDivisionId`, …) from the host's initial props *once*.
|
|
7
|
+
* After that, the store is authoritative for what's being edited:
|
|
8
|
+
* • Internal edit actions (`setDivisionContent`, `patchDivision`, `setTitle`,
|
|
9
|
+
* …) update the store optimistically and the host callbacks are fired
|
|
10
|
+
* purely as notifications (so the host can persist/autosave). A host is no
|
|
11
|
+
* longer required to echo every edit back as new props for it to display.
|
|
12
|
+
* • Genuine external updates (a save that reconciles server-assigned ids, or
|
|
13
|
+
* swapping to a different project) still win: Editors.tsx detects when a
|
|
14
|
+
* controlled prop actually changes since the last render and calls
|
|
15
|
+
* `applyExternalUpdate()` to overwrite the buffer. A stale prop that the
|
|
16
|
+
* host simply never updated is NOT re-applied, so it can't clobber a local
|
|
17
|
+
* edit.
|
|
18
|
+
*
|
|
19
|
+
* Derived/config fields that are never edited locally (`source`, `sourceFormat`,
|
|
20
|
+
* `projectAssets`, `projectType`, `rootDivisionId`, …) are still mirrored from
|
|
21
|
+
* props every render via `syncState()`.
|
|
10
22
|
*
|
|
11
23
|
* Callback stability: createEditorStore returns a `bindCallbacks` function that
|
|
12
24
|
* EditorsInner calls from useLayoutEffect after every render. Store actions
|
|
@@ -24,6 +36,20 @@ export type DivisionChanges = {
|
|
|
24
36
|
sourceFormat?: SourceFormat;
|
|
25
37
|
label?: string | null;
|
|
26
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* A batch of editing-buffer fields the host has genuinely changed (an external
|
|
41
|
+
* reset). Only the provided fields are overwritten in the store; omitted
|
|
42
|
+
* fields keep their current — possibly locally edited — value.
|
|
43
|
+
*/
|
|
44
|
+
export interface ExternalUpdate {
|
|
45
|
+
divisions?: Division[];
|
|
46
|
+
rootDivisionId?: string;
|
|
47
|
+
activeDivisionId?: string | null;
|
|
48
|
+
title?: string;
|
|
49
|
+
docinfo?: string;
|
|
50
|
+
commonDocinfo?: string;
|
|
51
|
+
useCommonDocinfo?: boolean;
|
|
52
|
+
}
|
|
27
53
|
type ModalKey = "isLatexDialogOpen" | "isConvertDialogOpen" | "isDocinfoEditorOpen" | "isAssetPickerOpen";
|
|
28
54
|
/**
|
|
29
55
|
* All callbacks wired by Editors.tsx that deep components need to call.
|
|
@@ -71,21 +97,36 @@ export interface EditorStoreState {
|
|
|
71
97
|
isConvertDialogOpen: boolean;
|
|
72
98
|
isDocinfoEditorOpen: boolean;
|
|
73
99
|
isAssetPickerOpen: boolean;
|
|
74
|
-
internalTitle: string;
|
|
75
|
-
internalDocinfo: string;
|
|
76
|
-
internalCommonDocinfo: string;
|
|
77
|
-
internalUseCommonDocinfo: boolean;
|
|
78
100
|
editingId: string | null;
|
|
79
101
|
editDraft: EditDraft | null;
|
|
80
102
|
/** Sync a batch of derived/controlled data from Editors into the store. */
|
|
81
103
|
syncState: (partial: Partial<EditorSyncableState>) => void;
|
|
104
|
+
/** Apply a genuine external update from the host (host wins). */
|
|
105
|
+
applyExternalUpdate: (partial: ExternalUpdate) => void;
|
|
106
|
+
/** Optimistically set a division's content in the local pool. */
|
|
107
|
+
setDivisionContent: (xmlId: string, content: string) => void;
|
|
108
|
+
/** Optimistically patch a division's metadata (title/type/xml:id/format). */
|
|
109
|
+
patchDivision: (xmlId: string, changes: DivisionChanges) => void;
|
|
110
|
+
/** Optimistically add a division to the local pool (no-op if it exists). */
|
|
111
|
+
addDivisionToPool: (division: Division) => void;
|
|
112
|
+
/** Optimistically remove a division from the local pool. */
|
|
113
|
+
removeDivisionFromPool: (xmlId: string) => void;
|
|
114
|
+
/** Set the active (open-for-editing) division id. */
|
|
115
|
+
setActiveDivisionId: (id: string | null) => void;
|
|
116
|
+
/** Optimistically set the document title. */
|
|
117
|
+
setTitle: (title: string) => void;
|
|
118
|
+
/** Optimistically set the docinfo-related fields together. */
|
|
119
|
+
setDocinfo: (info: {
|
|
120
|
+
docinfo: string;
|
|
121
|
+
commonDocinfo: string;
|
|
122
|
+
useCommonDocinfo: boolean;
|
|
123
|
+
}) => void;
|
|
82
124
|
setShowFullPreview: (show: boolean) => void;
|
|
83
125
|
setActiveTab: (tab: "editor" | "preview") => void;
|
|
84
126
|
setIsNarrowScreen: (narrow: boolean) => void;
|
|
85
127
|
setIsTocCollapsed: (value: boolean | ((prev: boolean) => boolean)) => void;
|
|
86
128
|
openModal: (modal: ModalKey) => void;
|
|
87
129
|
closeModal: (modal: ModalKey) => void;
|
|
88
|
-
setInternalTitle: (title: string) => void;
|
|
89
130
|
selectSection: (id: string) => void;
|
|
90
131
|
addSection: (afterId: string | null) => void;
|
|
91
132
|
removeSection: (id: string) => void;
|
|
@@ -102,7 +143,7 @@ export interface EditorStoreState {
|
|
|
102
143
|
feedbackSubmit: (feedback: FeedbackSubmission) => void;
|
|
103
144
|
}
|
|
104
145
|
/** The subset of EditorStoreState that Editors.tsx syncs on each render. */
|
|
105
|
-
export type EditorSyncableState = Pick<EditorStoreState, "source" | "sourceFormat" | "projectAssets" | "libraryAssets" | "title" | "docinfo" | "commonDocinfo" | "useCommonDocinfo" | "projectType" | "projectUrl" | "divisions" | "rootDivisionId" | "activeDivisionId" | "canConvertToPretext" | "activeEditorSource" | "hasFeedback"
|
|
146
|
+
export type EditorSyncableState = Pick<EditorStoreState, "source" | "sourceFormat" | "projectAssets" | "libraryAssets" | "title" | "docinfo" | "commonDocinfo" | "useCommonDocinfo" | "projectType" | "projectUrl" | "divisions" | "rootDivisionId" | "activeDivisionId" | "canConvertToPretext" | "activeEditorSource" | "hasFeedback">;
|
|
106
147
|
export interface EditorStoreInit {
|
|
107
148
|
source: string;
|
|
108
149
|
sourceFormat: SourceFormat;
|