diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5663257 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +node_modules/ +workplace/* +!workplace/.gitkeep diff --git a/README.md b/README.md new file mode 100644 index 0000000..1ff6f78 --- /dev/null +++ b/README.md @@ -0,0 +1,125 @@ +# miku-docx2md + +`miku-docx2md` は、Word の `.docx` ファイルを Markdown に変換するローカル実行ツールです。 + +見た目を Word どおりに再現するためのツールではありません。文章、見出し、リスト、表、リンクなどの文書構造を Markdown として読みやすく取り出すことを目的にしています。 + +## できること + +- `.docx` ファイルを Markdown に変換 +- ブラウザだけでローカル変換 +- Node.js CLI で変換 +- 見出し、段落、箇条書き、番号付きリスト、表を出力 +- 太字、斜体、取り消し線、下線を一部保持 +- 外部リンクと解決可能な文書内リンクを出力 +- 解決可能な埋め込み画像を sidecar asset として出力 +- 変換サマリーを表示または保存 +- debug 用に unsupported 要素の HTML comment trace を出力 + +## 使い方: ブラウザ + +`index.html` はランディングページです。変換本体は `miku-docx2md.html` で開きます。 + +1. `index.html` を開きます。 +2. `miku-docx2md.html` へ進みます。 +3. `.docx` ファイルを選択します。 +4. 変換ボタンを押します。 +5. Markdown と summary を確認します。 +6. 必要に応じて Markdown、summary、画像 asset ZIP をダウンロードします。 + +画像 asset ZIP は、変換結果に解決可能な埋め込み画像がある場合だけ利用できます。 + +## 使い方: CLI + +```bash +npm run cli -- ./sample.docx --out ./sample.md +``` + +summary も出力する例: + +```bash +npm run cli -- ./sample.docx --out ./sample.md --summary --summary-out ./sample.summary.txt +``` + +画像 asset も出力する例: + +```bash +npm run cli -- ./sample.docx --out ./sample.md --assets-dir ./sample.assets +``` + +debug comment も含める例: + +```bash +npm run cli -- ./sample.docx --out ./sample.md --debug +``` + +## CLI オプション + +| Option | Description | +| --- | --- | +| `--out ` | Markdown の出力先 | +| `--assets-dir ` | 解決可能な埋め込み画像 asset の出力先 | +| `--summary` | summary を標準出力へ表示 | +| `--summary-out ` | summary の出力先 | +| `--debug` | unsupported 要素の HTML comment trace を Markdown に含める | +| `--include-unsupported-comments` | `--debug` と同じ | +| `--help` | ヘルプを表示 | + +`--assets-dir` を指定すると、解決可能な画像は `word/media/example.png` のような package-relative path で保存されます。Markdown 側も、可能な場合は `[Image: ...]` placeholder ではなく相対 `![](...)` link を出力します。 + +asset 出力先には `manifest.json` も作成されます。manifest には asset path、media type、alt text、byte size、source trace、block index、document position が含まれます。 + +## 出力方針 + +`miku-docx2md` は、Word の見た目ではなく文書構造を優先します。 + +- Word のページレイアウトは再現しません。 +- 変換結果は GitHub-compatible Markdown / HTML に寄せます。 +- 表の結合セルは `←M←` と `↑M↑` の placeholder で簡略表現します。 +- 画像は本文内の完全再現ではなく、解決可能なものを asset として出力します。 +- unsupported 要素は通常 Markdown には出しません。 +- `--debug` 使用時のみ、unsupported 要素の trace を HTML comment として出します。 + +## 主な対応内容 + +| Content | Status | +| --- | --- | +| 段落 | 対応 | +| 見出し | 対応 | +| 太字、斜体、取り消し線、下線 | 一部対応 | +| 段落内改行 | 対応 | +| 外部リンク | 対応 | +| 解決可能な文書内リンク | 対応 | +| 箇条書き、番号付きリスト、ネスト | 対応 | +| 表 | 対応 | +| 表の結合セル | placeholder で簡略対応 | +| 埋め込み画像 | 解決可能なものを sidecar asset として出力 | +| Word の見た目の完全再現 | 非対応 | + +## ビルド + +```bash +npm run build +``` + +`index-src.html`、`miku-docx2md-src.html`、`src/ts/` から、配布用の `index.html`、`miku-docx2md.html`、`src/js/` を再生成します。 + +## テスト + +```bash +npm run test:unit +``` + +## 詳細ドキュメント + +- 利用者向け補足: [docs/usage.md](./docs/usage.md) +- 実文書での品質確認: [docs/quality-check.md](./docs/quality-check.md) +- 仕様と設計方針: [docs/docx2md-spec.md](./docs/docx2md-spec.md) +- 実装仕様: [docs/docx2md-impl-spec.md](./docs/docx2md-impl-spec.md) +- upstream 参照方針: [docs/upstream.md](./docs/upstream.md) + +## License + +Apache License 2.0 + +See [LICENSE](./LICENSE). diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..8aab696 --- /dev/null +++ b/TODO.md @@ -0,0 +1,121 @@ +# TODO + +## Phase 1: Project Skeleton + +- [x] Create `README.md` +- [x] Create `docs/docx2md-spec.md` +- [x] Create `docs/docx2md-impl-spec.md` +- [x] Add project package metadata and test scripts +- [x] Add `src/ts/` and `src/js/` skeleton +- [x] Add minimal node runtime loader + +## Phase 2: Core Parsing Foundation + +- [x] Implement ZIP expansion in-house +- [x] Implement XML utility helpers +- [x] Implement relationship path resolution +- [x] Implement minimal `.docx` document parser +- [x] Implement minimal Markdown renderer + +## Phase 3: First-Cut Features + +- [x] Paragraph extraction +- [x] Heading detection +- [x] Inline formatting +- [x] External hyperlinks +- [x] Internal hyperlinks +- [x] Lists with nesting +- [x] Tables +- [x] Merge placeholders `←M←` / `↑M↑` + +## Phase 4: Diagnostics + +- [x] Summary counts +- [x] Unsupported element diagnostics +- [x] Debug option for unsupported HTML comments + +## Phase 5: Tests + +- [x] Plain paragraph fixture +- [x] Heading fixture +- [x] Inline formatting fixture +- [x] External hyperlink fixture +- [x] Internal hyperlink fixture +- [x] Nested list fixture +- [x] Table fixture +- [x] Unsupported element debug fixture + +## Phase 6: Post First-Cut Refinement + +- [x] Normalize internal bookmark anchors and internal hyperlink fragments +- [x] Preserve list-like paragraphs inside table cells as simplified inline list text +- [x] Classify unsupported elements in more detail than raw local-name passthrough where useful +- [x] Preserve heading-like paragraphs inside table cells as simplified heading text +- [x] Expand implementation docs for style inheritance depth and direct-format precedence + +## Phase 7: Next Expansion Candidates + +- [x] Emit debug unsupported-comment traces for unsupported elements nested inside supported blocks +- [x] Allow limited plain-text extraction for `txbxContent` while keeping textbox layout unsupported +- [x] Emit placeholder-like debug traces for unsupported image references with relationship targets when resolvable +- [x] Add focused tests for nested unsupported traces in CLI output as needed + +## Phase 8: Richer Image Diagnostics + +- [x] Include image alt text in debug traces when drawing metadata exposes `descr` or `title` +- [x] Include image dimensions from drawing metadata in debug traces when `wp:extent` is available +- [x] Emit a minimal non-debug placeholder for images when meaningful alt text is available + +## Phase 9: Image Observability + +- [x] Count image references in conversion summary output +- [x] Distinguish resolved embedded images from generic drawing-like unsupported objects in summary output + +## Phase 10: Image Asset Export + +- [x] Expose resolved embedded image package entries as parse-result assets in Node-oriented flows +- [x] Count extracted image assets separately from image references in summary output +- [x] Add CLI support for exporting resolved embedded image assets into a sidecar directory +- [x] Emit relative `![](...)` image links in Markdown when sidecar asset export is enabled explicitly + +## Phase 11: Image Asset Metadata + +- [x] Prefer `[Content_Types].xml` declarations over extension inference when reporting exported image asset media types +- [x] Expose downloadable image asset export in the browser UI as a ZIP archive + +## Phase 12: Asset Export Packaging + +- [x] Include `manifest.json` in CLI and browser asset exports +- [x] Include source trace strings and owning block indexes in asset manifests +- [x] Include finer manifest document positions with block kind and trace index + +## Phase 13: Real Document Quality Pass + +- [ ] Collect a small local validation set of real `.docx` documents +- [ ] Run browser conversion against the validation set +- [ ] Run CLI conversion against the validation set +- [ ] Compare Markdown readability for headings, lists, links, tables, and image placeholders +- [ ] Review generated summaries for obviously wrong counts +- [ ] Review debug output for unsupported trace usefulness +- [ ] Verify image asset export and `manifest.json` on documents with embedded images +- [ ] Record recurring incompatibilities as focused fixtures or known limitations + +## Phase 14: Release Readiness + +- [ ] Re-read README from a first-time user perspective +- [ ] Re-read `docs/usage.md` for CLI and browser workflow accuracy +- [ ] Confirm generated `index.html` and `miku-docx2md.html` are in sync with source files +- [ ] Run `npm run build` +- [ ] Run `npm run test:unit` +- [ ] Prepare release notes from user-visible changes + +## Phase 15: Refactoring + +- [ ] Review `miku-xlsx2md` module boundaries before cutting files +- [ ] Split DOCX package loading and relationship/content-type resolution out of `core.ts` where useful +- [ ] Split document XML block parsing, inline run parsing, table parsing, and drawing/image extraction out of `document-parser.ts` +- [ ] Extract Markdown rendering/escaping helpers into focused modules instead of keeping rendering logic inside parser orchestration +- [ ] Extract asset manifest construction and browser ZIP packaging into focused modules +- [ ] Keep CLI and browser behavior unchanged while refactoring by adding or preserving focused regression tests +- [ ] Update `scripts/lib/docx2md-module-order.mjs` and generated `src/js/` order whenever TypeScript modules are split +- [ ] Re-run `npm run build` and `npm run test:unit` after each refactoring slice diff --git a/docs/docx2md-impl-spec.md b/docs/docx2md-impl-spec.md new file mode 100644 index 0000000..daab565 --- /dev/null +++ b/docs/docx2md-impl-spec.md @@ -0,0 +1,288 @@ +# docx2md Implementation Specification + +## 1. Document Role + +This document records the current implementation-aligned behavior of `docx2md`. + +It complements: + +- [README.md](../README.md) + - entry document for users and readers +- [docx2md-spec.md](./docx2md-spec.md) + - high-level specification and design policy +- [upstream.md](./upstream.md) + - policy for referring to the sibling upstream app `miku-xlsx2md` + +When implementation behavior differs from idealized design intent, this document should describe the implementation behavior explicitly. + +## 2. Current Implementation Scope + +The current first cut includes: + +- in-house `.docx` ZIP entry reading +- XML DOM parsing utilities +- relationship resolution for document hyperlinks +- style-based and outline-level heading detection +- inline formatting for bold, italic, strike, underline, and paragraph-internal `
` +- external hyperlinks and document-internal hyperlinks +- nested lists based on `numbering.xml` +- structural table extraction +- merge placeholders `←M←` and `↑M↑` +- summary counts +- unsupported-element diagnostics +- Node.js CLI +- browser UI based on `lht-cmn/` + +The current first cut still excludes: + +- image extraction +- drawing / shape extraction +- exact layout reproduction +- header / footer, footnotes, comments, tracked changes + +## 3. Overall Flow + +The current end-to-end flow is: + +1. read a `.docx` file as bytes +2. expand ZIP entries in-house +3. load `word/document.xml` +4. optionally load `word/_rels/document.xml.rels`, `word/styles.xml`, and `word/numbering.xml` +5. parse document blocks in document order +6. build a lightweight parsed document with `blocks` and `summary` +7. render Markdown +8. optionally emit summary text and unsupported debug comments + +## 4. ZIP Handling + +Current behavior: + +- ZIP expansion is performed by the project code rather than by an external ZIP library +- required entries are looked up by normalized package path +- `word/document.xml` is mandatory +- missing optional entries fall back to empty behavior rather than immediate failure + +If `word/document.xml` is missing, parsing fails with an explicit error. + +## 5. XML Utilities + +Current behavior: + +- XML bytes are decoded as UTF-8 text +- parsing uses `DOMParser` +- lookup is based on `localName` rather than namespace prefix spelling +- helper functions exist for direct-child lookup, descendant lookup, and text extraction + +Whitespace is not aggressively normalized at XML-read time. Most text normalization happens during inline extraction and Markdown rendering. + +## 6. Relationship Resolution + +Current behavior: + +- `word/_rels/document.xml.rels` is parsed into a map keyed by relationship id +- external hyperlink targets are preserved as-is +- internal package targets are resolved relative to `word/document.xml` +- missing relationship ids do not fail the whole conversion +- unresolved internal anchors fall back to plain link text rather than emitting broken Markdown fragments + +For hyperlink rendering: + +- `r:id` with a known external relationship becomes an external Markdown link +- `r:id` with an internal fragment target becomes an internal Markdown link only when the normalized target matches a known bookmark owner +- missing or broken relationship ids fall back to plain link text unless a `w:anchor` target is available + +## 7. Style and Heading Resolution + +Current behavior: + +- heading detection uses both paragraph style and outline level +- direct `Heading n` / `見出し n` recognition is supported +- style resolution walks the style chain returned by `styles.xml` +- style-chain heading names and outline levels are both considered +- paragraph-local `outlineLvl` acts as a fallback when style-based heading detection does not resolve a level +- cyclic `basedOn` chains are cut off safely during resolution + +Heading levels are clamped to Markdown heading range `1..6`. + +## 8. Inline Formatting + +Current behavior: + +- paragraph styles may contribute inherited text formatting +- paragraph-level direct run properties under `pPr/rPr` override inherited paragraph-style text formatting +- character styles referenced by `rStyle` may contribute inherited run formatting +- direct run formatting under `rPr` overrides both paragraph-derived and character-style-derived text formatting +- explicit `w:val="0"` / `false` on supported run-format flags disables inherited formatting for that scope +- run formatting supports bold, italic, strike, and underline +- wrapper order is: + - underline + - strike + - italic + - bold +- paragraph-internal `w:br` becomes `
` +- tabs are normalized to four spaces +- repeated spaces are compacted during inline normalization +- surrounding whitespace is trimmed at the resulting inline string level + +Hyperlink text suppresses underline wrapping so that link syntax is not nested with underline output. + +## 9. Hyperlinks and Anchors + +Current behavior: + +- external hyperlinks render as `[text](url)` +- internal hyperlinks with `w:anchor` render as `[text](#anchor)` only when the normalized anchor matches a known bookmark owner +- internal hyperlinks with relationship fragment targets such as `#anchor` follow the same known-anchor check +- paragraph bookmarks from `bookmarkStart` are collected as block anchor ids +- known-anchor resolution is limited to top-level document paragraphs that can emit block anchors +- duplicate normalized bookmark anchors are emitted only for the first owning block +- bookmark names starting with `_` are ignored +- known paragraph bookmarks render as HTML anchors before the block: + - `` +- anchor normalization trims whitespace, lowercases, collapses spaces to `-`, replaces unsupported punctuation with `-`, and collapses repeated `-` +- the same normalization is applied to bookmark owners and internal hyperlink targets so links remain aligned + +## 10. Numbering and Lists + +Current behavior: + +- `numbering.xml` is parsed into `abstractNum` and `num` mappings +- `numId` and `ilvl` are used to determine list kind and nesting depth +- bullet-like numbering becomes `-` +- ordered numbering becomes `1.` +- indentation width is `4 spaces` per nesting level + +If numbering metadata cannot be resolved, the paragraph falls back to ordinary paragraph behavior rather than forcing a synthetic list. + +## 11. Tables + +Current behavior: + +- tables are parsed in document order +- each row is read from `w:tr` +- each cell is read from `w:tc` +- cell text is built from paragraph content +- multiple paragraphs inside a cell are joined by `

` +- heading-like paragraphs inside table cells are preserved as simplified heading text such as `## Heading` +- list paragraphs inside table cells are preserved as simplified inline list text using `-` or `1.` +- nested list depth inside table cells is represented with repeated `    ` +- horizontal merge placeholders use `←M←` +- vertical merge continuation placeholders use `↑M↑` +- additional horizontally spanned cells inside a vertical continuation row use `←M←` +- shorter rows are padded with empty cells so the Markdown table stays rectangular + +Markdown rendering uses the first row as the header row. + +## 12. Markdown Rendering + +Current behavior: + +- paragraphs render as plain Markdown blocks +- headings render as `#` through `######` +- list items render with bullet or ordered markers and 4-space nesting +- tables render as Markdown tables +- unsupported blocks are omitted by default +- unsupported blocks render as HTML comments only when debug-style output is enabled + +Anchor rendering is inserted immediately before the owning paragraph, heading, or list item block. + +## 13. Summary and Diagnostics + +Current summary fields are: + +- `paragraphs` +- `headings` +- `listItems` +- `tables` +- `images` +- `imageAssets` +- `drawingLikeUnsupported` +- `links` +- `internalLinks` +- `externalLinks` +- `unsupportedElements` +- `unsupportedCommentTraces` + +`unsupportedCommentTraces` currently counts both standalone unsupported blocks and unsupported traces attached to supported blocks. + +Unsupported element traces currently use a small normalized category set for common cases: + +- `drawing` for drawing-like elements such as `drawing`, `pict`, and `object` +- `textbox` for textbox-like elements such as `txbxContent` +- `chart` for `chart` +- otherwise the raw `localName` is used + +When unsupported traces are rendered as debug HTML comments, comment-breaking sequences from source metadata are sanitized so the debug output does not prematurely close the comment. + +For drawing-like unsupported elements, when an embedded image relationship can be resolved safely, the current debug trace may include the package target in a form such as: + +- `drawing:image(word/media/example.png)` + +When drawing metadata exposes image alt text through attributes such as `descr` or `title`, the current debug trace may append that metadata in a form such as: + +- `drawing:image(word/media/example.png):alt(Example alt text)` + +Image trace parsing preserves alt text that contains ordinary parentheses. + +When drawing metadata exposes `wp:extent`, the current debug trace may append the EMU size in a form such as: + +- `drawing:image(word/media/example.png):size-emu(914400x457200)` + +When unsupported content is found inside a supported paragraph or table, the trace is attached to that owning block and rendered as an adjacent HTML comment only in debug-style output. + +Current textbox handling is a limited compromise: + +- `txbxContent` nested inside a supported block may contribute plain extracted paragraph text +- textbox paragraphs inside that extracted content may still be simplified as heading-like text or inline list-like text +- textbox layout, positioning, and shape semantics remain unsupported + +Current image handling is also a limited compromise: + +- normal Markdown output still does not embed extracted image files automatically +- when meaningful image alt text is available, normal Markdown may include a lightweight placeholder such as `[Image: Example alt text]` +- debug-style output still emits the fuller unsupported trace with relationship target and any available metadata +- the current Node-facing parse result may expose resolved embedded image package entries as `assets` +- the current CLI may export those resolved embedded image assets when `--assets-dir ` is specified +- when `--assets-dir ` is used, the current CLI also switches image placeholders to relative Markdown image links when an alt text is available +- generated Markdown image destinations are percent-escaped for characters that would otherwise break inline Markdown links, such as spaces and parentheses +- generated Markdown image alt text is collapsed to one line, and square brackets are removed only for image-link syntax +- the current asset metadata prefers `[Content_Types].xml` declarations when available and falls back to extension-based media-type inference otherwise +- current asset exports also include `manifest.json` with asset path, media type, alt text, byte size, originating unsupported trace, owning block index, and a finer `documentPosition` object with block kind and per-block trace index + +## 14. Browser UI and CLI + +### 14.1 Browser UI + +Current browser UI behavior: + +- uses `lht-cmn/` as the shared component base +- keeps `index.html` as the landing page and `miku-docx2md.html` as the conversion app page +- uses `lht-page-hero`, `lht-file-select`, `lht-switch-help`, `lht-preview-output`, `lht-loading-overlay`, `lht-error-alert`, and `lht-toast` +- allows file selection first and explicit conversion second +- supports Markdown save and summary save +- supports image-asset ZIP save when resolved embedded image assets are available +- browser image-asset ZIP export writes stored ZIP entries with CRC32 values and the UTF-8 filename flag +- uses `lht-preview-output` built-in copy actions for preview text +- toggles unsupported HTML comments via a switch + +Page-local CSS lives in `src/css/app.css`. + +### 14.2 Node.js CLI + +Current CLI options include: + +- `--out ` +- `--assets-dir ` +- `--summary` +- `--summary-out ` +- `--debug` +- `--include-unsupported-comments` +- `--help` + +`--debug` and `--include-unsupported-comments` currently enable the same Markdown behavior. + +## 15. Open Items + +The main remaining implementation questions are: + +- no major open items are currently recorded in this document diff --git a/docs/docx2md-spec.md b/docs/docx2md-spec.md new file mode 100644 index 0000000..9bb26c0 --- /dev/null +++ b/docs/docx2md-spec.md @@ -0,0 +1,585 @@ +# docx2md Specification + +## 1. Document Overview + +`docx2md` is a tool that reads Word documents in `.docx` format and converts their textual structure into Markdown. + +The goal is not to reproduce the visual appearance of Microsoft Word documents exactly. +The goal is to extract document structure and meaningful text in a form that is easy for humans to read and easy for generative AI systems to consume. + +This tool should be designed as: + +- `Word document structure extraction -> Markdown` + +not simply: + +- `Word appearance reproduction -> Markdown` + +This document describes the high-level specification and design policy. +Implementation-specific behavior should be documented separately in an implementation specification once the first cut exists. + +## 2. Scope + +### 2.1 Supported Input + +- `.docx` + +### 2.2 Unsupported Input + +The first cut does not target: + +- `.doc` +- `.rtf` +- `.odt` +- pasted rich text or clipboard input + +### 2.3 Supported Content in First Cut + +The first cut should focus on textual and structural content that maps cleanly into Markdown. + +- paragraphs +- headings +- inline text runs +- bold / italic / strike / underline +- line breaks inside paragraphs +- hyperlinks +- bullet lists +- numbered lists +- tables + +### 2.4 Out of Scope in First Cut + +The first cut intentionally excludes visual and layout-heavy features. + +- images +- shapes +- SmartArt +- WordArt +- text boxes +- floating objects +- charts +- drawing objects +- exact page layout reproduction +- headers / footers +- footnotes / endnotes +- comments +- tracked changes +- macros + +These may be considered later, but should not complicate the first implementation. + +## 3. Target Documents + +`docx2md` primarily targets document-style inputs whose main value is in the written content and document hierarchy. + +- specifications +- design documents +- manuals +- reports +- meeting notes +- narrative business documents + +## 4. Design Principles + +### 4.1 Output Purpose + +The Markdown output should aim to satisfy the following: + +- easy for humans to read +- easy for generative AI to process +- preserves meaningful structure +- preserves document order +- preserves enough source traceability to understand where content came from + +### 4.2 Conversion Policy + +- prioritize semantic structure over visual fidelity +- preserve document reading order +- map Word structure into ordinary Markdown where reasonable +- prefer stable and predictable output over aggressive interpretation +- keep the first cut small and testable +- prioritize features that can be expressed naturally in GitHub-compatible Markdown / HTML +- do not force representation for features that do not fit GitHub-compatible Markdown / HTML naturally + +In other words, the conversion policy is: + +- if a Word feature can be represented reasonably in GitHub-compatible Markdown / HTML, preserve it +- if it cannot be represented reasonably in that range, do not preserve it in the first cut + +This is a deliberate output policy, not merely an implementation limitation. + +### 4.3 Relationship to `miku-xlsx2md` + +`docx2md` may reuse ideas from the sibling app `miku-xlsx2md`, especially: + +- documentation structure +- separation of core logic and UI +- browser-first local processing +- optional CLI alignment +- test-first implementation style +- ZIP container handling approach +- XML parsing style +- TypeScript-first source management +- single-file web app packaging direction where practical +- `lht-cmn/` based browser UI composition + +Unless there is a clear `docx`-specific reason to differ, `docx2md` should imitate `miku-xlsx2md` in overall repository structure, implementation style, naming discipline, test style, and browser/runtime separation. +For browser UI, `docx2md` should treat `lht-cmn/` as the primary shared component layer and should prefer `lht-*` components over page-local widget implementations unless there is a clear reason to differ. + +However, `docx2md` should not inherit spreadsheet-specific behavior. +There is no table-region detection problem equivalent to Excel sheet analysis. +The main parsing targets are document order, paragraph style, numbering, runs, and tables. + +### 4.4 File and Module Split Policy + +The source file split should also imitate `miku-xlsx2md` where practical. + +- use `src/ts/` as the source-of-truth implementation directory +- keep generated `src/js/` output separate from TypeScript source +- keep `main.ts` as the browser UI entry point +- keep `core.ts` as the central conversion orchestration layer +- split helper logic into small focused modules instead of concentrating everything in one large parser file + +The split should remain reasonable for the smaller `docx` scope. +It is not necessary to copy the spreadsheet app file count mechanically, but the same style should be followed: + +- ZIP handling in a dedicated module such as `zip-io.ts` +- XML helper functions in a dedicated module such as `xml-utils.ts` +- relationship parsing in a dedicated module such as `rels-parser.ts` +- style parsing in a dedicated module such as `styles-parser.ts` +- numbering/list parsing in a dedicated module +- document parsing in a dedicated module +- Markdown rendering and export helpers in dedicated modules + +In short, prefer modest responsibility-based file splitting over monolithic implementation files, while avoiding artificial fragmentation. + +## 5. Output Unit and File Structure + +### 5.1 Output Unit + +The first cut should treat one `.docx` file as one input and produce one Markdown document as the primary output. + +### 5.2 Default Output + +The primary output should be: + +- one combined Markdown file + +Since images and shapes are out of scope in the first cut, ZIP export is not required initially. + +### 5.3 Naming + +The default output file name should be based on the input document name. + +Example: + +- input: `design.docx` +- output: `design.md` + +## 6. Parsing Model + +### 6.1 Container Handling + +A `.docx` file should be treated as a ZIP package. + +As with `miku-xlsx2md`, ZIP expansion should be implemented in-house from scratch rather than delegated to an external ZIP library. +The implementation should follow the same general direction as the sibling app: + +- read the package as raw bytes +- parse ZIP headers directly +- enumerate entries in a predictable way +- decode required XML entries from the extracted byte data +- keep the ZIP handling layer separate from document parsing + +This is intended to preserve architectural consistency with `miku-xlsx2md` and keep the core parsing stack understandable and testable. + +The first cut should read at least the following package entries when present: + +- `word/document.xml` +- `word/_rels/document.xml.rels` +- `word/styles.xml` +- `word/numbering.xml` + +### 6.2 Core Internal Model + +The internal model may remain small in the first cut. + +- document +- paragraph +- run +- hyperlink +- list item +- table +- table row +- table cell + +The first cut may also keep lightweight internal metadata for: + +- bookmark or anchor targets +- internal and external hyperlink targets +- unsupported element diagnostics + +### 6.3 Reading Order + +The parser should preserve the document order found in `word/document.xml`. +This is the primary structural axis for `docx2md`. + +## 7. Markdown Conversion Rules + +### 7.1 Paragraphs + +- ordinary paragraphs become plain Markdown paragraphs +- empty paragraphs may act as paragraph separators + +### 7.2 Headings + +- heading-like paragraph styles should map to Markdown headings +- `Heading 1` -> `#` +- `Heading 2` -> `##` +- `Heading 3` -> `###` +- deeper levels may continue similarly + +Heading detection in the first cut should use both paragraph style information and outline level information. +This combined approach is more robust than relying on only one of them. + +Recommended priority: + +1. heading-equivalent paragraph style +2. outline level +3. otherwise treat as a normal paragraph + +More specifically: + +- first resolve the paragraph style through `pStyle` and `styles.xml` +- if the resolved style is a built-in heading-equivalent style, use that heading level +- if the style does not resolve to a heading but the paragraph has a valid outline level, use the outline level +- if neither indicates a heading, do not treat the paragraph as a heading + +For compatibility, heading recognition should not depend only on display labels. +The implementation should prefer structural identifiers such as `styleId` and resolved style definitions. +Localized style names may be used only as a compatibility aid. + +First-cut compatibility should include at least the following heading-style families when they can be identified safely: + +- `Heading 1` to `Heading 6` +- `見出し 1` to `見出し 6` + +Heading level mapping should be: + +- level 1 -> `#` +- level 2 -> `##` +- level 3 -> `###` +- level 4 -> `####` +- level 5 -> `#####` +- level 6 or deeper -> `######` + +The first cut should not infer headings only from appearance. +The following alone are not enough to classify a paragraph as a heading: + +- larger font size +- bold text +- centered alignment +- spacing before or after the paragraph + +### 7.3 Inline Formatting + +- bold -> `**text**` +- italic -> `*text*` +- strike -> `~~text~~` +- underline -> `text` +- paragraph-internal line breaks -> `
` + +When multiple inline styles apply to the same text, the rendering order should follow the sibling-app approach for deterministic output. +Recommended wrapper application order is: + +1. underline +2. strike +3. italic +4. bold + +This means the final visible output places `bold` outermost when all four styles are active. + +### 7.4 Hyperlinks + +- external links should become Markdown links when possible +- format: `[text](url)` +- document-internal links should become `[text](#anchor)` when the target anchor is known +- when a paragraph owns a bookmark target, the Markdown output may emit a lightweight HTML anchor such as `` immediately before the block +- internal anchor names should be normalized into stable fragment-safe ids +- recommended normalization is: trim, lowercase, collapse whitespace to `-`, replace unsupported punctuation with `-`, and collapse repeated `-` +- unresolved internal links should fall back to plain link text rather than emitting a broken Markdown target +- when hyperlink text also has underline formatting, GitHub output does not need to add extra underline markup on top of the link +- document-internal links should also be preserved when the target can be resolved safely +- a resolved internal link may render as `[text](#anchor)` +- if an internal target cannot be resolved reliably, the implementation may fall back to plain text rather than emitting a broken link + +### 7.5 Lists + +- bullet lists should become `- item` +- numbered lists should become `1. item` +- nested lists are required in the first cut + +List handling in the first cut should use `numbering.xml` and paragraph numbering properties as the primary structural source. +The implementation should not rely on visual indentation alone. + +The first cut should support at least: + +- bullet lists +- numbered lists +- nested lists +- mixed nesting where a bullet list contains a numbered list or vice versa + +Recommended interpretation order: + +1. resolve the paragraph numbering reference from the paragraph properties +2. resolve the numbering instance through `numId` +3. resolve the abstract numbering definition +4. use the paragraph level such as `ilvl` to determine nesting depth +5. use the numbering definition to distinguish bullet-style and ordered-style items + +Markdown rendering policy: + +- unordered items should render as `- item` +- ordered items should render as `1. item` +- nested items should be indented in Markdown according to their nesting depth +- the output should prefer stable Markdown readability over reproducing Word numbering glyphs exactly + +The first cut does not need to reproduce every numbering style variation exactly. +For example, the following may be normalized while still preserving list structure: + +- `1.` / `1)` / `a.` / `A.` / `i.` / `I.` +- localized numbering markers +- custom bullet glyphs + +In such cases, preserving ordered vs unordered structure and nesting depth is more important than preserving the exact marker text. + +The first cut should also define clear limits: + +- list detection should be based on numbering structure, not indentation width alone +- paragraphs without list structure should not be converted into lists only because they are visually indented +- if numbering metadata is broken or incomplete, the implementation may fall back to a plain paragraph instead of inventing list structure + +### 7.6 Tables + +- simple Word tables should become Markdown tables +- table extraction is structural, not visual +- line breaks inside table cells should become `
` + +Merged cells should be simplified in the first cut using explicit merge placeholders rather than attempting HTML table reproduction. + +Recommended merge rendering policy: + +- the origin cell of a merge keeps its actual content +- a horizontally absorbed cell should render as `←M←` +- otherwise, a vertically absorbed cell should render as `↑M↑` + +In other words, merge placeholder priority should be: + +1. if there is a parent cell on the left, use `←M←` +2. otherwise, if there is a parent cell above, use `↑M↑` + +Additional first-cut table rules: + +- the Markdown table column count should follow the Word table grid as parsed +- the implementation should preserve table structure rather than attempting pixel-equivalent layout reproduction +- the first cut should not switch to raw HTML table output just to preserve merge layout + +### 7.7 Line Break and Whitespace Normalization + +Line break and whitespace normalization should prioritize stable Markdown output over layout-oriented fidelity. + +Document/block-level rules: + +- ordinary paragraphs should be separated by one blank line +- headings should be separated from surrounding blocks by one blank line +- tables should be separated from surrounding blocks by one blank line +- consecutive empty paragraphs with no meaningful content should be compressed into a single paragraph break +- leading empty paragraphs at the start of a document may be removed +- trailing empty paragraphs at the end of a document may be removed + +Paragraph-level rules: + +- explicit paragraph-internal line breaks should render as `
` +- multiple explicit line breaks may remain as repeated `
` +- paragraph text should be trimmed at both ends before final Markdown emission +- run boundaries should not insert guessed spaces automatically + +Whitespace normalization rules: + +- tabs should normalize to four spaces +- repeated ordinary spaces may be normalized to a single space in normal prose output +- normalization should preserve intentionally explicit line break structure +- whitespace normalization should favor deterministic output and test stability + +Cell-level rules: + +- a line break inside a table-cell paragraph should render as `
` +- multiple paragraphs inside one table cell may be joined using `

` +- table-cell text should be trimmed at both ends before final Markdown emission +- when a table-cell paragraph is structurally a heading, the output may preserve it as simplified heading text such as `## Heading` +- when a table-cell paragraph is structurally a list item, the output may preserve it as simplified inline list text such as `- item` or `1. item` +- nested list depth inside table cells may be represented with lightweight indentation rather than full block Markdown structure + +List-item text should follow the same general normalization policy as ordinary paragraphs unless a later implementation section defines a narrower exception. + +### 7.8 Style Resolution Depth + +The first cut should resolve only the style layers needed for structural extraction and supported inline formatting. + +Recommended priority order: + +1. direct formatting on the paragraph or run +2. character style +3. paragraph style +4. inherited style chain via `basedOn` + +Style resolution should be deep enough to support at least: + +- heading detection +- bold +- italic +- strike +- underline +- list-related paragraph structure when style metadata is relevant + +For supported text emphasis, the intended precedence is: + +1. direct run formatting +2. character style resolved through `rStyle` +3. paragraph-local run properties +4. paragraph style +5. inherited `basedOn` chain + +When a supported style flag is explicitly disabled at a narrower scope, that narrower scope should take precedence over inherited formatting. + +The first cut does not need to resolve every style-related visual detail. +It should prioritize structure and supported Markdown-facing emphasis rather than Word layout fidelity. + +Implementation safety rules: + +- `basedOn` chains may be followed recursively +- cyclic style references should be detected and cut off safely +- broken or missing style references should fall back gracefully to the next available source of information + +## 8. Error and Fallback Policy + +- unsupported visual features should be ignored rather than breaking the whole conversion +- parsing should fail clearly for invalid `.docx` inputs +- when a structure cannot be represented faithfully in Markdown, prefer a simple stable fallback +- when a Word feature does not map naturally to the supported GitHub-compatible Markdown/HTML subset, omission is preferable to awkward pseudo-reproduction + +This includes a deliberate output-policy compromise similar to `miku-xlsx2md`: + +- some rich text and display formatting should be preserved only when they fall naturally into GitHub-compatible Markdown / HTML +- formatting that does not fall naturally into that range should not be forced into artificial pseudo-syntax + +Unsupported elements may leave an HTML comment trace in the Markdown when that helps preserve document understanding without heavily disturbing readability. +However, this should be disabled by default in normal output. + +Recommended first-cut fallback policy for unsupported elements: + +- unsupported elements should not usually become visible prose in the Markdown body +- by default, unsupported elements should be omitted from the Markdown body +- when a debug-oriented switch is enabled, unsupported elements may be represented by lightweight HTML comments +- HTML comment traces should remain concise and diagnostic rather than verbose +- when an unsupported element is nested inside a supported block, the debug trace may be emitted adjacent to that owning block rather than as a standalone block + +The first cut may expose this through a dedicated option such as: + +- `--debug` +- `--include-unsupported-comments` + +The exact option name may be finalized later, but the policy should be: + +- default output: no unsupported-element HTML comments +- debug-oriented output: unsupported-element HTML comments allowed + +Examples of acceptable fallback direction: + +- `` +- `` +- `` + +When multiple XML spellings represent the same broad unsupported feature, the implementation may normalize them into one concise diagnostic category. + +An implementation may also choose a narrower compromise for text boxes: preserve plain textual paragraphs from `txbxContent` when they can be extracted safely, while still treating textbox layout and placement as unsupported. +For image-like drawing content, an implementation may keep the image unsupported while still emitting a debug-oriented placeholder trace that includes the resolved relationship target and, when safely available, metadata such as alt text (`descr` / `title`) and drawing extent. +When meaningful alt text is available, the implementation may also emit a small non-debug placeholder such as `[Image: ...]` without attempting actual image extraction or layout reproduction. +For Node-oriented workflows, an implementation may additionally expose resolved embedded image package entries as sidecar assets without attempting layout reconstruction or automatic Markdown image embedding. +When such sidecar asset export is enabled explicitly, the implementation may choose to replace that placeholder with a relative Markdown image link such as `![alt](assets/word/media/example.png)`. +When package content types are available, an implementation may prefer those declarations over file-extension inference when reporting exported asset media types. +An implementation may also include a small manifest file alongside exported assets so downstream tools can recover path, media-type, alt-text, and byte-size metadata without reparsing the source `.docx`. +When useful for diagnostics, that manifest may also include the originating unsupported trace string, the owning block index, and a finer document-position object such as block kind plus per-block trace index. + +The first cut should prefer concise comment traces over large raw XML dumps or long explanatory blocks. + +## 9. Summary and Diagnostics + +The first cut should maintain a lightweight conversion summary and unsupported-element diagnostics, following the general sibling-app philosophy of keeping conversion behavior observable. + +Recommended summary items include at least: + +- paragraphs +- headings +- listItems +- tables +- images +- imageAssets +- drawingLikeUnsupported +- links +- internalLinks +- externalLinks +- unsupportedElements +- unsupportedCommentTraces + +`unsupportedCommentTraces` does not need to equal only top-level unsupported blocks; it may also include traces attached to supported blocks that contained unsupported nested elements. + +## 10. First-Cut Test Coverage + +The first cut should be validated primarily through fixture-based tests. +The fixture set should be small but intentionally representative of the supported feature boundaries. + +Recommended first-cut coverage includes at least: + +- plain paragraph extraction +- heading detection from paragraph style and/or outline level +- inline formatting for bold, italic, strike, and underline +- explicit line breaks rendered as `
` +- external hyperlink extraction +- internal hyperlink extraction when the anchor target is resolvable +- bullet list extraction +- numbered list extraction +- nested list extraction +- mixed nested list extraction +- simple table extraction +- merged-cell placeholder rendering with `←M←` and `↑M↑` +- table-cell line breaks rendered as `
` +- unsupported objects leaving concise HTML comment traces when the debug-oriented switch is enabled +- graceful fallback when unresolved internal links or broken numbering/style metadata are encountered + +The first-cut test set should prioritize deterministic Markdown output. +Tests should prefer exact-output assertions for stable representative fixtures whenever practical. + +## 11. Runtime and Packaging Direction + +The intended direction is to follow the sibling app style where practical. + +- local processing +- browser-capable implementation +- TypeScript as source of truth +- testable core logic + +CLI support may be added later, but it does not need to block the first cut. + +## 12. Initial Development Priorities + +Recommended implementation order: + +1. ZIP entry reading for `.docx` +2. plain paragraph extraction +3. inline run formatting +4. headings +5. hyperlinks +6. lists via numbering +7. tables +8. summary and tests diff --git a/docs/quality-check.md b/docs/quality-check.md new file mode 100644 index 0000000..22d17d1 --- /dev/null +++ b/docs/quality-check.md @@ -0,0 +1,107 @@ +# 実文書での品質確認 + +この文書は、実際の `.docx` ファイルで変換品質を確認するための手順です。 + +## 目的 + +fixture test は既知の挙動を確認します。実文書での確認は、実用的な Word 文書で初めて見つかる変換ギャップを拾うために行います。 + +目的は Word の見た目を完全再現することではありません。文書構造が読みやすい Markdown として残ることを確認します。 + +## 検証用ドキュメント + +形の違う `.docx` を少数用意します。 + +推奨カテゴリ: + +- 簡単なメモや議事録 +- 複数レベルの見出しを含む文書 +- 箇条書き、番号付きリスト、ネスト list を含む文書 +- 表を含む文書 +- 文書内リンクと外部リンクを含む文書 +- 埋め込み画像を含む文書 +- 図形や chart など unsupported な視覚要素を含む文書 + +private または sensitive な文書は repository に commit しません。 + +## ブラウザ確認 + +各文書で次を確認します。 + +1. `index.html` を開きます。 +2. `miku-docx2md.html` へ進みます。 +3. `.docx` ファイルを選択します。 +4. 変換を実行します。 +5. Markdown preview を確認します。 +6. summary preview を確認します。 +7. unsupported comment を有効にして debug-oriented output を確認します。 +8. 画像 asset がある場合は download します。 + +確認観点: + +- 見出しが見出しとして読める +- 段落が読みやすい +- ネスト list の構造が残っている +- 表が矩形で読みやすい +- 文書内リンクが出力済み anchor だけを指している +- 外部リンクが残っている +- 画像 placeholder または画像 link が読みやすい +- unsupported comment が不足内容の診断に使える程度に簡潔 +- summary count が明らかにおかしくない + +## CLI 確認 + +各文書で実行します。 + +```bash +npm run cli -- ./sample.docx --out ./sample.md --summary --summary-out ./sample.summary.txt +``` + +画像を含む文書では asset 出力も確認します。 + +```bash +npm run cli -- ./sample.docx --out ./sample.md --assets-dir ./sample.assets --summary --summary-out ./sample.summary.txt +``` + +診断用出力も確認します。 + +```bash +npm run cli -- ./sample.docx --out ./sample.debug.md --debug +``` + +確認観点: + +- command が成功する +- Markdown file が作成される +- summary file が必要時に作成される +- asset directory が必要時に作成される +- asset export 時に `manifest.json` がある +- `manifest.json` に記録された画像 file が disk 上にある +- Markdown image link が export された asset path を指している + +## triage 方針 + +実文書で問題を見つけたら、code を変える前に分類します。 + +- Bug: 対応済みのはずの構造が誤って変換されている +- Missing fixture: 期待挙動はあるが test が足りない +- Known limitation: source feature が現在の scope 外 +- Documentation issue: 挙動は許容できるが説明が足りない + +bug を直す場合は、できるだけ focused fixture test を追加します。 + +## release gate + +release 前に実行します。 + +```bash +npm run build +npm run test:unit +``` + +あわせて確認します。 + +- README が現在の user-facing behavior と一致している +- `docs/usage.md` の例が現在も使える +- `index.html` と `miku-docx2md.html` が現在の source から生成されている +- private な検証用文書を commit していない diff --git a/docs/upstream.md b/docs/upstream.md new file mode 100644 index 0000000..a436258 --- /dev/null +++ b/docs/upstream.md @@ -0,0 +1,27 @@ +# Upstream Reference Policy + +## Purpose + +This repository develops `miku-docx2md` while referring to the sibling app `miku-xlsx2md` as an upstream design and implementation reference. + +## Upstream Repository + +The primary upstream reference is the Git clone placed at: + +- `workplace/miku-xlsx2md` + +Its origin is: + +- `https://github.com/igapyon/miku-xlsx2md` + +When reviewing architecture, documentation style, file layout, build scripts, or test strategy, prefer this clone as the canonical sibling reference. + +## Intended Usage + +- Read `workplace/miku-xlsx2md` first when considering `miku-docx2md` specifications. +- Reuse compatible ideas such as documentation structure, core/UI separation, CLI alignment, and test strategy. +- Do not assume `xlsx`-specific logic is directly reusable for `docx`; use it as a sibling reference, not as a direct specification. + +## Scope Note + +For `miku-docx2md` first cut, images and shapes are out of scope even though the upstream sibling app handles richer asset extraction in the spreadsheet domain. diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..e4d2480 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,106 @@ +# miku-docx2md 利用ガイド + +この文書は、README に置くには長い利用者向けの補足をまとめます。 + +## ブラウザで使う + +ローカルのブラウザで変換したい場合は、ランディングページの `index.html` から本体の `miku-docx2md.html` を開きます。 + +1. `index.html` を開きます。 +2. `miku-docx2md.html` へ進みます。 +3. `.docx` ファイルを選択します。 +4. 変換を実行します。 +5. Markdown と summary を確認します。 +6. 必要に応じて Markdown、summary text、画像 asset をダウンロードします。 + +ブラウザ版は、選択したローカルファイルをブラウザ UI 上で処理します。 + +## CLI で使う + +Markdown を出力します。 + +```bash +npm run cli -- ./sample.docx --out ./sample.md +``` + +summary を標準出力へ表示します。 + +```bash +npm run cli -- ./sample.docx --out ./sample.md --summary +``` + +summary をファイルへ保存します。 + +```bash +npm run cli -- ./sample.docx --out ./sample.md --summary-out ./sample.summary.txt +``` + +解決可能な画像 asset も出力します。 + +```bash +npm run cli -- ./sample.docx --out ./sample.md --assets-dir ./sample.assets +``` + +unsupported 要素の debug comment も出力します。 + +```bash +npm run cli -- ./sample.docx --out ./sample.md --debug +``` + +## 画像 asset 出力 + +`--assets-dir ` を指定すると、解決可能な埋め込み画像が asset directory 配下へ保存されます。 + +出力 path の例: + +- `manifest.json` +- `word/media/example.png` + +可能な場合、Markdown 内の画像 placeholder は相対画像 link に変換されます。 + +asset 出力なし: + +```markdown +[Image: Example alt text] +``` + +asset 出力あり: + +```markdown +![Example alt text](sample.assets/word/media/example.png) +``` + +## asset manifest + +asset 出力には `manifest.json` が含まれます。 + +manifest には次の情報が入ります。 + +- asset kind +- source package path +- media type +- alt text +- source trace +- owning block index +- finer document position +- byte size + +`[Content_Types].xml` がある場合、拡張子による推定より package-declared content type を優先します。 + +## debug comment + +通常、unsupported 要素は Markdown に出力されません。 + +`--debug` または `--include-unsupported-comments` を使うと、簡潔な HTML comment が出力されます。 + +例: + +```markdown + +``` + +debug comment は診断用です。最終的な文章として読ませる用途ではありません。 + +## 実文書で確認する + +実際の `.docx` ファイルで変換品質を確認する場合は、[quality-check.md](./quality-check.md) の手順を使います。 diff --git a/index-src.html b/index-src.html new file mode 100644 index 0000000..435c36f --- /dev/null +++ b/index-src.html @@ -0,0 +1,270 @@ + + + + + + + + + miku-docx2md + + + + +
+
Local Browser Tool
+

miku-docx2md

+

Version 0.8.0 / Updated 2026-04-24

+ +
+

What is this?

+

+ miku-docx2md は、Word の .docx ファイルをローカルで読み込み、文書構造を Markdown として抽出するツールです。 +

+
    +
  • ブラウザ内で動作し、サーバ通信を行いません。
  • +
  • 段落、見出し、リスト、表、リンクを Markdown として取り出します。
  • +
  • 解決可能な埋め込み画像は sidecar asset として出力できます。
  • +
+
+ +
+ +
+

Use Cases

+
    +
  • Word 文書を生成AIに渡しやすい Markdown に変換したい。
  • +
  • 仕様書、議事録、手順書などの文書構造を再利用したい。
  • +
  • 機密文書をアップロードせず、手元のブラウザだけで処理したい。
  • +
+
+ +
+ +
+

How to use

+
    +
  1. Web ブラウザで miku-docx2md.html を開きます。
  2. +
  3. .docx ファイルを選択します。
  4. +
  5. 変換を実行し、Markdown と summary を確認します。
  6. +
  7. Markdown、summary、画像 asset ZIP を必要に応じて保存します。
  8. +
+ +
+ +
+ +
+

Features

+
+
+

Markdown conversion

+

見出し、段落、inline formatting、リンク、list、table を Markdown / HTML として出力します。

+
+
+

Asset export

+

解決可能な埋め込み画像を ZIP または CLI の sidecar directory として出力できます。

+
+
+

Diagnostics

+

summary と debug comment で、unsupported 要素や画像参照を確認できます。

+
+
+

Local processing

+

ブラウザ版はローカルで処理します。CLI も Node.js でローカル実行できます。

+
+
+
+
+ + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..435c36f --- /dev/null +++ b/index.html @@ -0,0 +1,270 @@ + + + + + + + + + miku-docx2md + + + + +
+
Local Browser Tool
+

miku-docx2md

+

Version 0.8.0 / Updated 2026-04-24

+ +
+

What is this?

+

+ miku-docx2md は、Word の .docx ファイルをローカルで読み込み、文書構造を Markdown として抽出するツールです。 +

+
    +
  • ブラウザ内で動作し、サーバ通信を行いません。
  • +
  • 段落、見出し、リスト、表、リンクを Markdown として取り出します。
  • +
  • 解決可能な埋め込み画像は sidecar asset として出力できます。
  • +
+
+ +
+ +
+

Use Cases

+
    +
  • Word 文書を生成AIに渡しやすい Markdown に変換したい。
  • +
  • 仕様書、議事録、手順書などの文書構造を再利用したい。
  • +
  • 機密文書をアップロードせず、手元のブラウザだけで処理したい。
  • +
+
+ +
+ +
+

How to use

+
    +
  1. Web ブラウザで miku-docx2md.html を開きます。
  2. +
  3. .docx ファイルを選択します。
  4. +
  5. 変換を実行し、Markdown と summary を確認します。
  6. +
  7. Markdown、summary、画像 asset ZIP を必要に応じて保存します。
  8. +
+ +
+ +
+ +
+

Features

+
+
+

Markdown conversion

+

見出し、段落、inline formatting、リンク、list、table を Markdown / HTML として出力します。

+
+
+

Asset export

+

解決可能な埋め込み画像を ZIP または CLI の sidecar directory として出力できます。

+
+
+

Diagnostics

+

summary と debug comment で、unsupported 要素や画像参照を確認できます。

+
+
+

Local processing

+

ブラウザ版はローカルで処理します。CLI も Node.js でローカル実行できます。

+
+
+
+
+ + + + diff --git a/lht-cmn/FEEDBACK.md b/lht-cmn/FEEDBACK.md new file mode 100755 index 0000000..deb3c6c --- /dev/null +++ b/lht-cmn/FEEDBACK.md @@ -0,0 +1,54 @@ +# lht-cmn Feedback + +## 2026-03-08 `lht-command-block` style contract gap + +- 症状: + - `lht-command-block` をページ側で利用しても、`lht-cmn/css/components.css` だけでは「角丸四角の結果表示ブロック」として視覚的に完成しない + - 実際には `md-code-block` / `md-code` / `md-copy-button` / `md-icon-button.md-copy-button` 相当の見た目をページ側 CSS が別途持っている前提になっている +- 問題: + - `lht-command-block` を共通部品として使っても、利用ページごとに結果表示の見た目が欠けうる + - `lht-cmn` の self-contained 方針とずれている +- 期待: + - `lht-command-block` は `lht-cmn/css/components.css` だけで最低限の完成した見た目になるべき + - 少なくとも以下の visual contract は `lht-cmn` 側に同梱する + - `.md-code-block` + - `.md-code` + - `.md-copy-button` + - `md-icon-button.md-copy-button` + - `md-icon-button.md-copy-button--surface` +- 補足: + - 今回 `docs/prompt/prompt-gen-src.html` では、既存画面に合わせるためページ側へ上記スタイルを追加して回避した + - 根本対応は `lht-cmn` 側で行うべき + +## 2026-03-08 `lht-switch-help` material bundle gap + +- 症状: + - `lht-switch-help` を利用しても、ページ側で `md-switch` が未登録のため fallback 実装に落ちる + - `prompt-gen` では text field は Material 表示なのに switch だけ fallback 表示になる +- 問題: + - `lht-*` を使っても、入力部品ごとに Material / fallback が混在しやすい + - ページ側から見ると `lht-switch-help` の見た目が他の Material 部品と揃わず、利用側で原因が見えにくい +- 期待: + - `lht-switch-help` も `lht-cmn` 側で Material 実装を self-contained に利用できる形に寄せたい + - 少なくとも `md-switch` 用 bundle の vendor / 読み込み導線を `lht-cmn` 側で用意したい + - それが難しい場合でも、README に「switch は fallback 前提になりうる」ことを明記したい +- 補足: + - 現状の `lht-switch-help` は `window.customElements.get("md-switch")` が false のとき fallback DOM を生成する実装になっている + - `lht-cmn/vendor` には `material-web-outlined-text-field.bundle.js` はあるが、`md-switch` 相当の bundle は見当たらない + +## 2026-03-09 `lht-text-field-help` trailing action gap + +- 症状: + - `prompt-gen` で「やりたいこと」入力欄に `×` クリアボタンを付けたかったが、`lht-text-field-help` 自体には trailing action / trailing icon を安全に差し込む契約がない + - 外付け absolute 配置では、Material 側の見た目中心と合いにくく、位置合わせが不安定だった +- 問題: + - 画面ごとに似た「入力クリア」「末尾アイコン」「補助アクション」実装が再発しやすい + - `lht-text-field-help` を使っていても、入力欄内アクションはページ側の局所 CSS に依存しやすい + - fallback 実装と Material 実装で、末尾アクションの見た目や余白が揃いにくい +- 期待: + - `lht-text-field-help` に trailing action slot、または `clearable` のような共通機能を検討したい + - もし汎用化しない場合でも、「入力欄右端に後付けアクションを重ねる時の推奨パターン」を README に明記したい +- 補足: + - 今回は暫定対応として `lht-text-field-help` に `clearable` 属性をローカル追加し、`prompt-gen` ではそれを利用する形へ寄せた + - ただしこれは prompt-gen 都合で先に入れた provisional API なので、`lht-cmn` チーム側では trailing action 全般を扱える正式な契約として見直したい + - 正式方針が固まったら、今回の `clearable` 実装や CSS 調整はレビューのうえ置き換えたい diff --git a/lht-cmn/LICENSE b/lht-cmn/LICENSE new file mode 100755 index 0000000..4bb44d2 --- /dev/null +++ b/lht-cmn/LICENSE @@ -0,0 +1,197 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with the Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lht-cmn/NOTICE b/lht-cmn/NOTICE new file mode 100755 index 0000000..d1b7fb9 --- /dev/null +++ b/lht-cmn/NOTICE @@ -0,0 +1,11 @@ +Copyright 2026 Toshiki Iga + +This product includes software developed by Toshiki Iga. + +Design direction: +- 本プロジェクトは Material Design 3 の設計原則を参照しています。 + +Third-party component implementation: +- `lht-cmn` の内部実装では、必要に応じて Material Web(`@material/web`)を優先利用します。 +- Material Web のライセンスは Apache License, Version 2.0 です。 +- Source: https://github.com/material-components/material-web diff --git a/lht-cmn/README.md b/lht-cmn/README.md new file mode 100755 index 0000000..e7dad3c --- /dev/null +++ b/lht-cmn/README.md @@ -0,0 +1,408 @@ +# lht-cmn + +`lht-cmn` は `local-html-tools` 全体で共有する UI コンポーネント基盤です。 + +- Version: `v20260308` +- License: Apache License 2.0 (`lht-cmn/LICENSE`) +- Copyright: Toshiki Iga + +## ライセンスと帰属 + +- `lht-cmn` 自体は Apache License 2.0 で配布します。 +- デザイン方針は Material Design 3 の設計原則を参照します。 +- 実装技術として、`lht-cmn` は必要に応じて Material Web(`@material/web`)を優先利用します。 +- Material Web のライセンスは Apache License 2.0 です。 +- 帰属情報の詳細は `lht-cmn/NOTICE` に記載します。 + +## 目的 + +`local-html-tools` では、入力・選択・ヘルプ・コピー・メニューなどの UI を複数ページで繰り返し実装してきました。 +`lht-cmn` はこの重複を減らし、UI を `lht-*` Web Components として共通化するためのレイヤーです。 + +## 基本方針 + +- デザイン基準は Material Design 3 +- 画面側の公開 UI レイヤーは常に `lht-*` とし、`md-*` を直接使わせない +- `lht-*` は self-contained を原則とし、アプリ側へ `md-*` の登録責務を漏らさない +- 実現手段として Material Web を優先利用してよい +- ただし Material 利用の有無に関わらず、`lht-*` は最低保証で壊れないことを優先する +- 内部実装として許可する型は次の 2 つに限定する: + - `md-*` 優先 + fallback + - 完全自前実装 +- 「Material 依存で fallback なし」は原則避け、採る場合は README に明示する +- fallback は Material の完全再現ではなく、公開 API の最低保証に留める + +## メリット + +- 画面ごとの重複実装を削減できる +- 見た目と挙動(必須表示、ヘルプ表示、フォーカス時の挙動)を統一できる +- 変更点を `lht-cmn` に集約でき、保守・レビューがしやすくなる +- 単一HTML生成前提でも、開発時の部品再利用性を維持できる +- 変更点が局所化され、生成AIが誤って別画面を壊す確率が下がる +- UI規約が `lht-*` に集約され、提案が毎回同じ型で出せる +- レビュー時に「画面の見た目差分」より「共通部品の差分」を見ればよくなり、判断が速くなる + +## 運用方針(重要) + +- 画面側(`docs/*-src.html`)は `lht-*` を利用し、`md-*` 直接実装の追加は原則避ける +- `lht-cmn/js/components.js` を共通コンポーネントの正本とする +- `lht-cmn/css/components.css` を実運用スタイルの正本とする +- `lht-cmn/` 配下(特に `js/components.js` / `css/components.css`)の変更は、必ずユーザーの明示許可を得てから実施する +- `md3/` は段階的にリファレンス用途へ縮退し、実運用スタイルは `lht-cmn` に集約する + +## 構成 + +- `lht-cmn/js/components.js` + - 共通 Web Components 定義 +- `lht-cmn/css/components.css` + - 上記コンポーネントの共通スタイル +- `lht-cmn/catalog/index.html` + - 実表示と HTML 利用例を並べて確認するコンポーネントカタログ + +### コンポーネント一覧 + +| コンポーネント | できること | 内部構造(概要) | +|---|---|---| +| `lht-help-tooltip` | `(i)` ヘルプアイコンとツールチップを1タグで配置できる | `md-icon-button` が使える環境ではそれを利用し、未定義時はネイティブ `button` fallback を生成。タグ内HTMLをツールチップ本文へ差し込む | +| `lht-text-field-help` | ラベル付き入力(単一行/複数行)とフォーカス時ヘルプ表示を共通化できる | `md-outlined-text-field` が使える環境ではそれを利用し、未定義時はネイティブ `input` / `textarea` fallback を生成。属性(`field-id`/`label`/`type`/`rows` など)を透過する | +| `lht-select-help` | ラベル付きドロップダウンとヘルプ表示を共通化できる | 内部で `md-outlined-select` を生成し、`` を使用する +3. `lht-select-help` で `