@hwp-editor/react 1.0.0-rc.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/LICENSE +21 -0
- package/README.md +129 -0
- package/dist/index.cjs +1778 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +392 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +471 -0
- package/dist/index.d.ts +471 -0
- package/dist/index.js +1737 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Young Joon Lee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# @hwp-editor/react
|
|
2
|
+
|
|
3
|
+
Embeddable React UI for Korean HWP/HWPX documents, built on
|
|
4
|
+
[`@hwp-editor/core`](../core). All document work (read/render/edit/compose/
|
|
5
|
+
validate) goes through the `HwpEngine` interface — this package contains UI
|
|
6
|
+
only.
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
```tsx
|
|
11
|
+
import { HwpEditor } from "@hwp-editor/react";
|
|
12
|
+
import "@hwp-editor/react/style.css"; // required: styles are not auto-injected
|
|
13
|
+
|
|
14
|
+
<HwpEditor
|
|
15
|
+
engine={engine} // HwpEngine (e.g. createHttpEngine from core)
|
|
16
|
+
file={document} // DocumentHandle | null
|
|
17
|
+
onChange={(doc) => ...} // document bytes changed (apply / undo / compose)
|
|
18
|
+
onDirtyChange={(dirty) => ...}
|
|
19
|
+
/>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Editing model: segment-based structured editing. Rendered pages are the
|
|
23
|
+
visual canvas; clicking selects a segment; the side panel builds typed
|
|
24
|
+
`EditOp`s; the apply button commits all queued ops in one `engine.edit()`
|
|
25
|
+
call and re-renders; the undo button restores the snapshot taken before the
|
|
26
|
+
last applied edit. Both buttons are labelled from the active locale table,
|
|
27
|
+
so referring to them by a fixed label goes stale the moment the copy or the
|
|
28
|
+
locale changes. Protected/distribution documents open read-only with a notice
|
|
29
|
+
(engine `capabilities().editable === false`).
|
|
30
|
+
|
|
31
|
+
Keyboard: `Escape` clears the selection; `Cmd/Ctrl+Enter` applies pending ops.
|
|
32
|
+
|
|
33
|
+
## Props
|
|
34
|
+
|
|
35
|
+
| Prop | Type | Notes |
|
|
36
|
+
| ---- | ---- | ----- |
|
|
37
|
+
| `engine` | `HwpEngine` | Required. Every read/render/edit/compose/validate call goes through it. |
|
|
38
|
+
| `file` | `DocumentHandle \| null` | Required. `null` shows the empty state with the new-document entry point. |
|
|
39
|
+
| `locale` | `"en" \| "ko"` | UI chrome language. Defaults to `"en"`. Read at mount and on change, and also drives the editor root's `lang` attribute. Document content and engine-authored error prose are never translated. |
|
|
40
|
+
| `messages` | `Partial<MessageTable>` | Per-key overrides applied on top of the locale table. Merge order is `en` -> locale table -> `messages`, so a key you do not list keeps its locale default. An unknown key is a compile error. Import `en` / `ko` from this package to discover the keys. |
|
|
41
|
+
| `readOnly` | `boolean` | Forces the editor read-only regardless of what the engine reports, and never re-enables a document the engine reports as protected. **A UI affordance, not an authorization boundary** - it hides and disables controls in one browser, and a host that must actually prevent writes enforces that server-side, in front of the route handler. `@hwp-editor/server` provides that hook: `authorize(req, action)` refuses a request with HTTP 403 before any body is read, and its return value is also the tenant scope every server-side cache key is salted with. See [its trust boundary section](../server/README.md#trust-boundary). |
|
|
42
|
+
| `onReady` | `(document: DocumentHandle) => void` | Fires once per completed document load, after both the read and the render resolve, with the document that was loaded. Fires again on every `file` change. Never fires for `file={null}` or for a load cancelled by unmount. |
|
|
43
|
+
| `onError` | `(error: unknown) => void` | Fires for every engine failure (load, apply, undo, refresh, compose) carrying the caught value **verbatim**, so a host can branch on the stable `code` of a `HwpEngineError`. The inline alert renders either way; this is an addition to it, not a replacement. |
|
|
44
|
+
| `onChange` | `(document: DocumentHandle) => void` | Fires whenever the document bytes change: an applied edit, an undo, or a compose. |
|
|
45
|
+
| `onDirtyChange` | `(dirty: boolean) => void` | Fires when the dirty flag (pending ops queued) transitions. |
|
|
46
|
+
| `className` | `string` | Applied to the editor root. |
|
|
47
|
+
| `style` | `CSSProperties` | Applied to the editor root; the usual place to set `--hwped-*` variables. |
|
|
48
|
+
|
|
49
|
+
`locale` is a name collision worth stating plainly: this prop is the UI
|
|
50
|
+
chrome language and has nothing to do with `createCliEngine({ locale })` in
|
|
51
|
+
`@hwp-editor/server`, which sets the hwp-cli child process's `HWP_LANG`.
|
|
52
|
+
Setting one does not set the other.
|
|
53
|
+
|
|
54
|
+
## Imperative handle
|
|
55
|
+
|
|
56
|
+
`ref` exposes exactly four methods and no state getters, because hosts
|
|
57
|
+
observe state through the callbacks above, which stay in sync with React
|
|
58
|
+
rendering in a way a getter read from outside the render cycle cannot.
|
|
59
|
+
|
|
60
|
+
| Method | Returns | No-op when |
|
|
61
|
+
| ------ | ------- | ---------- |
|
|
62
|
+
| `apply()` | `Promise<void>` | The queue is empty, no document is loaded, an apply is already in flight, or the editor is read-only. Resolves without touching the engine. |
|
|
63
|
+
| `revert()` | `Promise<void>` | The snapshot stack is empty. Resolves without touching the engine. |
|
|
64
|
+
| `refresh()` | `Promise<void>` | Never a no-op: it re-reads and re-renders the current document after an external change. **Rejects** if the engine fails, after firing `onError` and showing the alert. |
|
|
65
|
+
| `openCompose()` | `void` | Never; it opens the new-document compose dialog. |
|
|
66
|
+
|
|
67
|
+
Host callbacks are non-reactive: passing a fresh inline arrow on every
|
|
68
|
+
render is fine and does not re-trigger a document load. The component holds
|
|
69
|
+
the latest identity behind a ref, so you do not need `useCallback` and you
|
|
70
|
+
never receive a stale closure.
|
|
71
|
+
|
|
72
|
+
## Styling contract
|
|
73
|
+
|
|
74
|
+
Zero component-kit imports, zero Tailwind, zero runtime CSS-in-JS. All
|
|
75
|
+
classes carry the `.hwped` prefix. Hosts theme ONLY via CSS variables, set on
|
|
76
|
+
the editor root or any ancestor:
|
|
77
|
+
|
|
78
|
+
| Variable | Purpose |
|
|
79
|
+
| ---------------- | ------------------------------------ |
|
|
80
|
+
| `--hwped-bg` | surface background |
|
|
81
|
+
| `--hwped-fg` | primary text |
|
|
82
|
+
| `--hwped-muted` | secondary text / disabled |
|
|
83
|
+
| `--hwped-accent` | primary actions, selection highlight |
|
|
84
|
+
| `--hwped-border` | hairline borders |
|
|
85
|
+
| `--hwped-radius` | corner radius |
|
|
86
|
+
| `--hwped-font` | font stack (default: Pretendard -> system) |
|
|
87
|
+
|
|
88
|
+
Defaults ship in `theme.css` with a `prefers-color-scheme: dark` fallback.
|
|
89
|
+
|
|
90
|
+
## Components
|
|
91
|
+
|
|
92
|
+
- `HwpEditor` — top level: toolbar (apply/revert/validation badge/pending
|
|
93
|
+
count/protected notice/new document) | `PageCanvas` | side panel tabs.
|
|
94
|
+
- `PageCanvas` — multi-page scroll; SVG inline render (sanitized:
|
|
95
|
+
script/foreignObject/event handlers stripped) with `<img>` fallback;
|
|
96
|
+
click -> nearest segment; highlight bands for the selected segment and
|
|
97
|
+
segments covered by pending ops.
|
|
98
|
+
- `SegmentInspector` — replace text, insert para before/after, delete para,
|
|
99
|
+
set-align, set-format (bold/size/color subset).
|
|
100
|
+
- `TableGrid` — cell selection, set-cell, add/delete row/col, merge/split
|
|
101
|
+
cell, delete-table. Addressing follows the CLI's 0-based
|
|
102
|
+
`table:row:col` grammar (row 0 = header row).
|
|
103
|
+
- `FieldsPanel` — lists `{{name}}` placeholder slots from the read()
|
|
104
|
+
envelope; set-field editing; click jumps to the containing segment.
|
|
105
|
+
- `ComposePanel` — preset picker (official/report/plan/notice/minutes/
|
|
106
|
+
gaejosik/press) + guided form -> DocumentSpec v2 -> `engine.compose()`.
|
|
107
|
+
|
|
108
|
+
## Known limitations (engine contract)
|
|
109
|
+
|
|
110
|
+
- **Hit-testing is approximate.** Neither the core segment envelope nor the
|
|
111
|
+
hwp-cli SVG renderer exposes per-paragraph page geometry. Clicks and
|
|
112
|
+
highlight bands use a linear text-flow model (see `src/geometry.ts`); the
|
|
113
|
+
highlight is the exact inverse of the click mapping, so they always agree.
|
|
114
|
+
A future hwp-cli layout API can replace this without UI changes.
|
|
115
|
+
- **FieldsPanel lists template slots, not native fields.** The engine
|
|
116
|
+
contract has no `hwp fields` / `hwp bookmarks` route, so the panel works
|
|
117
|
+
with `{{name}}` placeholders in the markdown.
|
|
118
|
+
- **Table structure is read from GFM markdown.** Merged cells are flattened
|
|
119
|
+
to their anchor cell text; merge/split ops still address the real HWP
|
|
120
|
+
table via CLI coordinates.
|
|
121
|
+
|
|
122
|
+
## Develop
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
pnpm install
|
|
126
|
+
pnpm -r build
|
|
127
|
+
pnpm -r test
|
|
128
|
+
pnpm -r typecheck
|
|
129
|
+
```
|