@wonseok-han/confluence-sync 1.8.0 → 2.0.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/README.md +44 -410
- package/dist/cli/args.js +1 -1
- package/dist/cli/help.js +214 -115
- package/dist/cli/init.js +44 -5
- package/dist/cli/main.js +42 -11
- package/dist/cli/status.js +83 -0
- package/dist/confluence/client.js +121 -57
- package/dist/confluence/config.js +12 -8
- package/dist/confluence/env-file.js +23 -0
- package/dist/confluence/links.js +18 -0
- package/dist/conversion/asset-paths.js +5 -0
- package/dist/conversion/attachments.js +29 -7
- package/dist/conversion/convert.js +10 -22
- package/dist/conversion/footnotes.js +114 -11
- package/dist/conversion/markdown.js +26 -0
- package/dist/conversion/pdf-pages.js +10 -8
- package/dist/documents/anchors.js +9 -0
- package/dist/documents/callout-roundtrip.js +75 -0
- package/dist/documents/callouts.js +74 -0
- package/dist/documents/catalog.js +2 -2
- package/dist/documents/footnote-roundtrip.js +96 -0
- package/dist/documents/format.js +116 -0
- package/dist/documents/html-tables.js +30 -0
- package/dist/documents/html-to-markdown.js +47 -13
- package/dist/documents/markdown.js +46 -4
- package/dist/documents/obsidian.js +6 -3
- package/dist/documents/tables.js +89 -0
- package/dist/runtime/network.js +4 -0
- package/dist/shared/colors.js +2 -2
- package/dist/shared/progress.js +164 -0
- package/dist/shared/version.js +12 -0
- package/dist/site/assets/Monaco-LICENSE.txt +21 -0
- package/dist/site/assets/Monaco-ThirdPartyNotices.txt +448 -0
- package/dist/site/assets/PDFJS-LICENSE.txt +177 -0
- package/dist/site/assets/app.css +1 -0
- package/dist/site/assets/app.js +4 -0
- package/dist/site/assets/chunks/chunk-TEW74QPN.js +35 -0
- package/dist/site/assets/chunks/markdown-S6623E66.js +1 -0
- package/dist/site/assets/chunks/monaco-36ZGUH4Y.js +897 -0
- package/dist/site/assets/chunks/monaco-HFLEBJTI.css +1 -0
- package/dist/site/assets/chunks/pdf-DDN7TXTP.js +55 -0
- package/dist/site/assets/confluence-env-example.txt +32 -0
- package/dist/site/assets/editor.worker.js +1 -0
- package/dist/site/assets/favicon.svg +3 -0
- package/dist/site/assets/guide/01-account-settings.png +0 -0
- package/dist/site/assets/guide/02-atlassian-security.png +0 -0
- package/dist/site/assets/guide/03-api-tokens.png +0 -0
- package/dist/site/assets/guide/04-scope-api-token-step1.png +0 -0
- package/dist/site/assets/guide/05-scope-api-token-step2.png +0 -0
- package/dist/site/assets/guide/06-scope-api-token-step3.png +0 -0
- package/dist/site/assets/guide/07-scope-api-token-step4.png +0 -0
- package/dist/site/assets/guide/web/document-preview.png +0 -0
- package/dist/site/assets/guide/web/remote-diff.png +0 -0
- package/dist/site/assets/guide/web/sync-workspace.png +0 -0
- package/dist/site/assets/pdf/cmaps/78-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78ms-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/78ms-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/83pv-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/90ms-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/90ms-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/90msp-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/90msp-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/90pv-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/90pv-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Add-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Add-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Add-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Add-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-0.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-1.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-3.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-4.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-5.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-6.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-0.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-1.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-3.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-4.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-5.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-0.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-1.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-3.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-4.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-5.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-6.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Korea1-0.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Korea1-1.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Korea1-2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/B5pc-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/B5pc-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/CNS-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/CNS-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/CNS1-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/CNS1-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/CNS2-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/CNS2-V.bcmap +3 -0
- package/dist/site/assets/pdf/cmaps/ETHK-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/ETHK-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/ETen-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/ETen-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/ETenms-B5-H.bcmap +3 -0
- package/dist/site/assets/pdf/cmaps/ETenms-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Ext-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Ext-RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Ext-RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Ext-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GB-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GB-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GB-H.bcmap +4 -0
- package/dist/site/assets/pdf/cmaps/GB-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBK-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBK-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBK2K-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBK2K-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBKp-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBKp-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBT-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBT-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBT-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBT-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBTpc-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBTpc-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBpc-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/GBpc-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKdla-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKdla-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKdlb-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKdlb-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKgccs-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKgccs-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKm314-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKm314-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKm471-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKm471-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKscs-B5-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/HKscs-B5-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Hankaku.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Hiragana.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSC-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSC-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSC-Johab-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSC-Johab-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSCms-UHC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSCms-UHC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSCpc-EUC-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/KSCpc-EUC-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Katakana.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/LICENSE +36 -0
- package/dist/site/assets/pdf/cmaps/NWP-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/NWP-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/RKSJ-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/RKSJ-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/Roman.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UCS2-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UCS2-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UTF16-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UTF16-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UTF8-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniCNS-UTF8-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UCS2-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UCS2-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UTF16-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UTF16-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UTF8-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniGB-UTF8-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UCS2-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UCS2-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UTF16-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UTF16-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UTF8-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS-UTF8-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UCS2-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UCS2-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UTF16-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UTF16-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UTF32-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UTF32-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UTF8-H.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/UniKS-UTF8-V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/V.bcmap +0 -0
- package/dist/site/assets/pdf/cmaps/WP-Symbol.bcmap +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitDingbats.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitFixed.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitFixedBold.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitFixedItalic.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitSerif.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitSerifBold.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitSerifItalic.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/FoxitSymbol.pfb +0 -0
- package/dist/site/assets/pdf/standard_fonts/LICENSE_FOXIT +27 -0
- package/dist/site/assets/pdf/standard_fonts/LICENSE_LIBERATION +363 -0
- package/dist/site/assets/pdf/standard_fonts/LiberationSans-Bold.ttf +0 -0
- package/dist/site/assets/pdf/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
- package/dist/site/assets/pdf/standard_fonts/LiberationSans-Italic.ttf +0 -0
- package/dist/site/assets/pdf/standard_fonts/LiberationSans-Regular.ttf +0 -0
- package/dist/site/assets/pdf/wasm/LICENSE_JBIG2 +196 -0
- package/dist/site/assets/pdf/wasm/LICENSE_OPENJPEG +39 -0
- package/dist/site/assets/pdf/wasm/LICENSE_PDFJS_JBIG2 +13 -0
- package/dist/site/assets/pdf/wasm/LICENSE_PDFJS_OPENJPEG +22 -0
- package/dist/site/assets/pdf/wasm/LICENSE_PDFJS_QCMS +19 -0
- package/dist/site/assets/pdf/wasm/LICENSE_QCMS +21 -0
- package/dist/site/assets/pdf/wasm/jbig2.wasm +0 -0
- package/dist/site/assets/pdf/wasm/jbig2_nowasm_fallback.js +15 -0
- package/dist/site/assets/pdf/wasm/openjpeg.wasm +0 -0
- package/dist/site/assets/pdf/wasm/openjpeg_nowasm_fallback.js +17 -0
- package/dist/site/assets/pdf/wasm/qcms_bg.wasm +0 -0
- package/dist/site/assets/pdf/wasm/quickjs-eval.js +16 -0
- package/dist/site/assets/pdf/wasm/quickjs-eval.wasm +0 -0
- package/dist/site/assets/pdf.worker.min.mjs +29 -0
- package/dist/site/assets/workspace.worker.js +409 -0
- package/dist/site/index.html +253 -0
- package/dist/sync/baseline.js +45 -0
- package/dist/sync/changes.js +51 -0
- package/dist/sync/diff.js +80 -0
- package/dist/sync/fetch.js +287 -0
- package/dist/sync/ignore.js +4 -4
- package/dist/sync/local-state.js +12 -0
- package/dist/sync/mapping.js +31 -8
- package/dist/sync/metadata.js +57 -0
- package/dist/sync/prepare.js +52 -0
- package/dist/sync/pull.js +618 -141
- package/dist/sync/push.js +260 -213
- package/dist/sync/reconcile.js +51 -0
- package/dist/sync/references.js +2 -2
- package/dist/sync/remap.js +17 -5
- package/dist/sync/render.js +3 -2
- package/dist/sync/restore.js +28 -0
- package/dist/sync/scope.js +47 -0
- package/dist/sync/staging.js +193 -0
- package/dist/sync/workspace.js +100 -0
- package/dist/web/assets/PDFJS-LICENSE.txt +177 -0
- package/dist/web/assets/app.css +1 -1
- package/dist/web/assets/app.js +3 -938
- package/dist/web/assets/chunks/chunk-TEW74QPN.js +35 -0
- package/dist/web/assets/chunks/markdown-S6623E66.js +1 -0
- package/dist/web/assets/chunks/monaco-36ZGUH4Y.js +897 -0
- package/dist/web/assets/chunks/monaco-HFLEBJTI.css +1 -0
- package/dist/web/assets/chunks/pdf-DDN7TXTP.js +55 -0
- package/dist/web/assets/confluence-env-example.txt +32 -0
- package/dist/web/assets/editor.worker.js +1 -30
- package/dist/web/assets/favicon.svg +3 -0
- package/dist/web/assets/guide/01-account-settings.png +0 -0
- package/dist/web/assets/guide/02-atlassian-security.png +0 -0
- package/dist/web/assets/guide/03-api-tokens.png +0 -0
- package/dist/web/assets/guide/04-scope-api-token-step1.png +0 -0
- package/dist/web/assets/guide/05-scope-api-token-step2.png +0 -0
- package/dist/web/assets/guide/06-scope-api-token-step3.png +0 -0
- package/dist/web/assets/guide/07-scope-api-token-step4.png +0 -0
- package/dist/web/assets/guide/web/document-preview.png +0 -0
- package/dist/web/assets/guide/web/remote-diff.png +0 -0
- package/dist/web/assets/guide/web/sync-workspace.png +0 -0
- package/dist/web/assets/pdf/cmaps/78-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78ms-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/78ms-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/83pv-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/90ms-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/90ms-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/90msp-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/90msp-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/90pv-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/90pv-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Add-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Add-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Add-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Add-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-0.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-1.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-3.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-4.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-5.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-6.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-0.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-1.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-3.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-4.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-5.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-0.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-1.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-3.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-4.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-5.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-6.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Korea1-0.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Korea1-1.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Korea1-2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/B5pc-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/B5pc-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/CNS-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/CNS-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/CNS1-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/CNS1-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/CNS2-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/CNS2-V.bcmap +3 -0
- package/dist/web/assets/pdf/cmaps/ETHK-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/ETHK-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/ETen-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/ETen-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/ETenms-B5-H.bcmap +3 -0
- package/dist/web/assets/pdf/cmaps/ETenms-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Ext-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Ext-RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Ext-RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Ext-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GB-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GB-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GB-H.bcmap +4 -0
- package/dist/web/assets/pdf/cmaps/GB-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBK-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBK-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBK2K-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBK2K-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBKp-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBKp-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBT-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBT-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBT-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBT-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBTpc-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBTpc-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBpc-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/GBpc-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKdla-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKdla-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKdlb-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKdlb-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKgccs-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKgccs-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKm314-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKm314-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKm471-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKm471-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKscs-B5-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/HKscs-B5-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Hankaku.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Hiragana.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSC-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSC-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSC-Johab-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSC-Johab-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSCms-UHC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSCms-UHC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSCpc-EUC-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/KSCpc-EUC-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Katakana.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/LICENSE +36 -0
- package/dist/web/assets/pdf/cmaps/NWP-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/NWP-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/RKSJ-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/RKSJ-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/Roman.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UCS2-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UCS2-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UTF16-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UTF16-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UTF8-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniCNS-UTF8-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UCS2-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UCS2-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UTF16-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UTF16-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UTF8-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniGB-UTF8-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UCS2-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UCS2-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UTF16-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UTF16-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UTF8-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS-UTF8-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UCS2-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UCS2-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UTF16-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UTF16-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UTF32-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UTF32-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UTF8-H.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/UniKS-UTF8-V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/V.bcmap +0 -0
- package/dist/web/assets/pdf/cmaps/WP-Symbol.bcmap +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitDingbats.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitFixed.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitFixedBold.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitFixedItalic.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitSerif.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitSerifBold.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitSerifItalic.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/FoxitSymbol.pfb +0 -0
- package/dist/web/assets/pdf/standard_fonts/LICENSE_FOXIT +27 -0
- package/dist/web/assets/pdf/standard_fonts/LICENSE_LIBERATION +363 -0
- package/dist/web/assets/pdf/standard_fonts/LiberationSans-Bold.ttf +0 -0
- package/dist/web/assets/pdf/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
- package/dist/web/assets/pdf/standard_fonts/LiberationSans-Italic.ttf +0 -0
- package/dist/web/assets/pdf/standard_fonts/LiberationSans-Regular.ttf +0 -0
- package/dist/web/assets/pdf/wasm/LICENSE_JBIG2 +196 -0
- package/dist/web/assets/pdf/wasm/LICENSE_OPENJPEG +39 -0
- package/dist/web/assets/pdf/wasm/LICENSE_PDFJS_JBIG2 +13 -0
- package/dist/web/assets/pdf/wasm/LICENSE_PDFJS_OPENJPEG +22 -0
- package/dist/web/assets/pdf/wasm/LICENSE_PDFJS_QCMS +19 -0
- package/dist/web/assets/pdf/wasm/LICENSE_QCMS +21 -0
- package/dist/web/assets/pdf/wasm/jbig2.wasm +0 -0
- package/dist/web/assets/pdf/wasm/jbig2_nowasm_fallback.js +15 -0
- package/dist/web/assets/pdf/wasm/openjpeg.wasm +0 -0
- package/dist/web/assets/pdf/wasm/openjpeg_nowasm_fallback.js +17 -0
- package/dist/web/assets/pdf/wasm/qcms_bg.wasm +0 -0
- package/dist/web/assets/pdf/wasm/quickjs-eval.js +16 -0
- package/dist/web/assets/pdf/wasm/quickjs-eval.wasm +0 -0
- package/dist/web/assets/pdf.worker.min.mjs +29 -0
- package/dist/web/assets/workspace.worker.js +409 -0
- package/dist/web/document-source.js +14 -0
- package/dist/web/hosted-files.js +189 -0
- package/dist/web/hosted.js +316 -0
- package/dist/web/node-runner.js +26 -0
- package/dist/web/preview.js +74 -0
- package/dist/web/server.js +85 -240
- package/dist/web/sync.js +364 -70
- package/dist/web/table-editor-script.js +99 -0
- package/dist/web/table-format.js +224 -0
- package/dist/web/views/help.js +15 -10
- package/dist/web/views/sync-panel.js +90 -19
- package/dist/web/views/token-guide.js +27 -0
- package/dist/web/views/usage-guide.js +12 -0
- package/dist/web/views/workspace.js +85 -15
- package/dist/web/workspace-api.js +371 -0
- package/docs/user/CLI.md +366 -0
- package/docs/user/CONFIGURATION.md +125 -0
- package/docs/user/OBSIDIAN.md +117 -0
- package/docs/user/WEB.md +90 -0
- package/docs/user/images/web/document-preview.png +0 -0
- package/docs/user/images/web/remote-diff.png +0 -0
- package/docs/user/images/web/sync-workspace.png +0 -0
- package/package.json +23 -6
|
@@ -1,33 +1,69 @@
|
|
|
1
|
+
import { readPkgVersion } from '../../shared/version.js';
|
|
1
2
|
import { helpDialog } from './help.js';
|
|
2
3
|
import { syncPanel } from './sync-panel.js';
|
|
4
|
+
const formatIcon = (path) => `<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="${path}"/></svg>`;
|
|
3
5
|
/** HTML shell; Monaco and application code are bundled as local assets. */
|
|
4
|
-
export const webPage = (token) => `<!doctype html>
|
|
5
|
-
<html lang="ko"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="csync-token" content="${token}"><title>문서 워크스페이스 · confluence-sync</title><link rel="stylesheet" href="/assets/app.css"></head><body>
|
|
6
|
-
<header class="app-header"><a class="brand" href="/" aria-label="confluence-sync 홈"><
|
|
7
|
-
<main class="app-main">
|
|
8
|
-
<div class="page-heading"><div><div class="eyebrow">DOCUMENT WORKSPACE</div><h1>문서 워크스페이스<span class="title-dot">.</span></h1></div><div class="page-heading-actions"><p>문서를 변환하고, Confluence
|
|
9
|
-
<div class="mode-tabs" role="tablist" aria-label="문서 작업"><button id="tab-convert" role="tab" aria-selected="true" aria-controls="convert-view">문서 변환</button><button id="tab-sync" role="tab" aria-selected="false" aria-controls="sync-view" tabindex="-1">Confluence
|
|
6
|
+
export const webPage = (token, runtime = 'local') => `<!doctype html>
|
|
7
|
+
<html lang="ko"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="csync-token" content="${token}"><meta name="csync-runtime" content="${runtime}"><meta name="csync-version" content="${readPkgVersion()}"><title>문서 워크스페이스 · confluence-sync</title><link rel="icon" type="image/svg+xml" href="/assets/favicon.svg?v=paperlink"><link rel="stylesheet" href="/assets/app.css"></head><body>
|
|
8
|
+
<header class="app-header"><a class="brand" href="/" aria-label="confluence-sync 홈"><img class="brand-symbol" src="/assets/favicon.svg?v=paperlink" alt="" aria-hidden="true" width="30" height="30">confluence<span class="brand-light">sync</span></a><span class="app-version" aria-label="현재 버전">v${readPkgVersion()}</span><span class="local-status"><i></i><span id="local-connection-status">${runtime !== 'local' ? '브라우저 폴더 선택 필요' : token ? '로컬 워크스페이스' : '로컬 실행기 연결 필요'}</span></span><button id="local-disconnect" class="quiet" type="button" hidden>연결 해제</button></header>
|
|
9
|
+
<main id="workspace-interface" class="app-main">
|
|
10
|
+
<div class="page-heading"><div><div class="eyebrow">DOCUMENT WORKSPACE</div><h1>문서 워크스페이스<span class="title-dot">.</span></h1></div><div class="page-heading-actions"><p>문서를 변환하고, Confluence에서 가져오거나 업로드하세요.</p><button id="open-help" aria-haspopup="dialog" aria-controls="help-dialog">사용 도움말</button></div></div>
|
|
11
|
+
<div class="mode-tabs" role="tablist" aria-label="문서 작업"><button id="tab-convert" role="tab" aria-selected="true" aria-controls="convert-view">문서 변환</button><button id="tab-sync" role="tab" aria-selected="false" aria-controls="sync-view" tabindex="-1">Confluence</button></div>
|
|
10
12
|
<div class="workspace">
|
|
11
13
|
<aside class="panel explorer" aria-label="파일 탐색">
|
|
12
|
-
<div class="panel-heading"><h2
|
|
13
|
-
<div class="folder-picker
|
|
14
|
-
|
|
14
|
+
<div class="panel-heading"><h2>로컬 문서</h2><span id="count" class="count">폴더 탐색</span></div>
|
|
15
|
+
<div class="folder-picker root-picker">
|
|
16
|
+
<section class="workspace-root-card" aria-labelledby="root-heading">
|
|
17
|
+
<div class="workspace-root-heading"><h3 id="root-heading">기준 폴더</h3><span id="root-purpose" class="root-purpose">변환 기준</span></div>
|
|
18
|
+
<strong id="root-name" class="root-name">폴더를 선택하세요</strong>
|
|
19
|
+
<p id="root-path" class="root-path">아직 설정되지 않았습니다.</p>
|
|
20
|
+
<p id="root-hint" class="root-hint">문서와 참조 링크를 찾는 기준입니다.</p>
|
|
21
|
+
</section>
|
|
22
|
+
<button id="choose-base" class="folder-button"><span aria-hidden="true">↻</span>기준 폴더 변경…</button>
|
|
23
|
+
<details id="root-path-editor" class="root-path-editor"><summary>경로를 직접 입력해 변경</summary><label for="base-input">새 기준 폴더 경로</label><input id="base-input" class="path-input" placeholder="폴더의 전체 경로"><button id="apply-base" type="button">이 폴더를 기준으로 설정</button></details>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="explorer-navigation">
|
|
26
|
+
<div class="explorer-nav-heading"><span>문서 탐색</span><button id="base-home" class="quiet" type="button" aria-label="기준 폴더로 돌아가기">기준 폴더로</button></div>
|
|
27
|
+
<div class="explorer-nav-row"><button id="up" class="quiet" aria-label="상위 폴더" title="상위 폴더">↑</button><p id="path" class="path" aria-label="현재 탐색 위치"></p></div>
|
|
28
|
+
</div>
|
|
15
29
|
<div class="search-wrap"><span aria-hidden="true">⌕</span><input id="search" type="search" placeholder="하위 폴더까지 파일 이름 검색" aria-label="파일 이름 검색"></div>
|
|
16
|
-
<div id="files" class="files"></div
|
|
30
|
+
<div id="files" class="files"></div>
|
|
31
|
+
<div class="explorer-footer"><span class="file-type">MD</span><span id="explorer-note">파일·폴더를 선택해 변환하세요</span></div>
|
|
17
32
|
</aside>
|
|
18
33
|
<div id="convert-view" class="workspace-content" role="tabpanel" aria-labelledby="tab-convert">
|
|
34
|
+
<section id="document-preview-panel" class="panel document-preview" aria-label="문서 미리보기">
|
|
35
|
+
<div class="panel-heading document-preview-heading"><h2><button id="document-preview-toggle" class="preview-disclosure" aria-expanded="false" aria-controls="document-preview-body"><span id="document-preview-chevron" aria-hidden="true">▸</span>문서 미리보기<span id="document-preview-toggle-label" class="preview-disclosure-label">펼치기</span></button></h2><div id="document-preview-actions" class="actions" hidden><button id="document-format-toggle" class="quiet format-entry" aria-expanded="false" aria-controls="document-format-controls" disabled>${formatIcon('M4 5h16M4 10h10M4 15h8m4-2 4 4m-5 3 5-5 2 2-5 5h-2v-2Z')}문서 서식 정리</button><button id="document-preview-refresh" class="quiet" title="문서를 다시 읽기" disabled>${formatIcon('M20 7v5h-5M4 17v-5h5M6 7a7 7 0 0 1 12-1l2 3M4 15l2 3a7 7 0 0 0 12-1')}새로고침</button></div></div>
|
|
36
|
+
<div id="document-preview-body" hidden>
|
|
37
|
+
<p id="document-preview-status" class="document-preview-status" role="status" aria-live="polite">Markdown 파일을 선택하면 본문이 표시됩니다.</p>
|
|
38
|
+
<div id="document-format-controls" class="document-format-controls" hidden>
|
|
39
|
+
<div class="format-toolbar" role="group" aria-label="문서 서식 정리 도구">
|
|
40
|
+
<div class="format-tool-group"><span class="format-group-label">문서</span><button id="document-format-basic" class="format-tool-button" title="제목·문단 간격, 목록 기호, 공백과 Markdown 표 정리 결과를 비교합니다" disabled>${formatIcon('M4 5h16M4 10h16M4 15h10M4 20h10')}기본 서식 정리</button></div>
|
|
41
|
+
<div class="format-tool-group"><label class="format-group-label" for="document-format-tables">모든 표</label><select id="document-format-tables" title="문서 안의 모든 표 열 너비"><option value="keep">기존 너비 유지</option><option value="fit">내용에 맞춤</option><option value="equal">균등 배분</option></select><button id="table-edit-toggle" class="format-tool-button" aria-pressed="false" aria-controls="table-format-controls" disabled>${formatIcon('M3 4h18v16H3zM3 9h18M9 4v16M15 4v16')}표 직접 편집</button></div>
|
|
42
|
+
<div class="format-toolbar-actions"><button id="table-revert-all" class="quiet" title="모든 표 너비 변경 취소" disabled>${formatIcon('M4 4v6h6M4 10a8 8 0 1 1 1 8')}표 변경 취소</button><button id="document-format-review" class="primary" disabled>${formatIcon('M9 4H4v16h5M15 4h5v16h-5M9 9l-3 3 3 3M15 9l3 3-3 3')}변경 비교</button></div>
|
|
43
|
+
</div>
|
|
44
|
+
<div id="table-format-controls" class="table-format-controls" hidden>
|
|
45
|
+
<div class="format-context-heading"><span class="format-context-label">선택한 표</span><p id="table-format-status" role="status" aria-live="polite">미리보기에서 조절할 표를 클릭하세요.</p></div>
|
|
46
|
+
<div class="table-format-actions"><label>표 너비 <input id="table-total-width" type="range" min="320" max="1200" step="1" disabled><output id="table-total-label"></output></label><div class="format-button-set"><button id="table-fit" title="선택한 표의 글자 길이에 맞춰 열 너비 배분" disabled>내용에 맞춤</button><button id="table-equal" disabled>균등 배분</button></div><button id="table-revert" class="quiet" disabled>선택 표 취소</button></div>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="format-toolbar-footer"><span id="format-summary" role="status">변경 비교 후 원본 파일에 저장합니다.</span><details><summary>적용 범위</summary><p>기본 서식 정리를 누르면 정리 결과를 바로 비교합니다. 코드·링크 경로·HTML 표는 보존하며 저장 전에는 파일을 바꾸지 않습니다. 모든 표의 너비를 일괄 배분한 뒤 개별 표를 추가로 조절할 수 있습니다. 너비를 지정한 Markdown 표는 HTML로 바뀝니다. 비교창을 닫으면 저장하지 않습니다.</p></details></div>
|
|
49
|
+
</div>
|
|
50
|
+
<iframe id="document-preview-frame" class="document-preview-frame" title="선택한 Markdown 문서 미리보기" sandbox="allow-scripts" hidden></iframe>
|
|
51
|
+
<p class="document-preview-note">로컬 문서 미리보기이며 Confluence의 실제 표시와 다를 수 있습니다.</p>
|
|
52
|
+
</div>
|
|
53
|
+
</section>
|
|
19
54
|
<section class="panel settings" aria-label="변환 설정">
|
|
20
|
-
<div class="panel-heading"><h2><span
|
|
55
|
+
<div class="panel-heading"><h2>변환 설정</h2><span id="convert-save-mode" class="subtle-label">별도 폴더 저장</span></div>
|
|
21
56
|
<div class="settings-body">
|
|
22
57
|
<div class="document-summary"><span class="document-symbol" aria-hidden="true">MD</span><div><div id="selected" class="selected-name">변환할 문서를 선택하세요</div><div id="base" class="base">왼쪽에서 폴더를 열어 시작하세요.</div></div></div>
|
|
23
|
-
<
|
|
58
|
+
<div class="convert-target-actions"><button id="convert-select-all" type="button" disabled>기준 폴더 전체 선택</button><span id="convert-target-note" class="hint">파일·폴더를 선택하거나 기준 폴더 전체를 선택하세요.</span></div>
|
|
59
|
+
<p id="convert-output-hint" class="hint"></p><div class="settings-grid"><div class="field"><label for="direction">변환 형식</label><select id="direction"><option value="markdown">Markdown</option><option value="obsidian">Obsidian</option><option value="repair">Confluence 오류 보정만</option></select></div><div class="field output-field"><label for="out-input">출력 폴더 <span id="out-mode-hint">선택 사항</span></label><div class="path-row"><input id="out-input" class="path-input" placeholder="자동으로 새 폴더 생성"><button id="choose-out">폴더 선택…</button></div></div></div>
|
|
24
60
|
<div class="settings-notes"><p id="direction-hint" class="hint">위키링크를 상대 링크로, PDF 페이지 참조를 이미지로 변환합니다.</p><div class="repair-help"><label class="repair-option"><input id="fix" type="checkbox">Confluence 변환 오류 보정</label><button id="repair-help-button" class="info-mark" type="button" aria-label="Confluence 변환 오류 보정 설명" aria-describedby="repair-tooltip">i</button><div id="repair-tooltip" class="help-tooltip" role="tooltip" hidden>옛 Confluence 문서의 CSS 잔해, 중복 제목, 코드블록 언어와 불필요한 이스케이프를 보정합니다. 미리보기에서 변경 내용을 확인하세요.</div></div></div>
|
|
25
61
|
</div>
|
|
26
|
-
<div class="action-bar"><span class="save-note">선택한 대상과 참조 첨부파일을 저장합니다.</span><div class="actions"><button id="next-document" class="quiet" disabled>다른 문서 선택</button><button id="preview" disabled
|
|
62
|
+
<div class="action-bar"><span class="save-note">선택한 대상과 참조 첨부파일을 저장합니다.</span><div class="actions"><button id="next-document" class="quiet" disabled>다른 문서 선택</button><button id="preview" disabled>변환 미리보기</button><button id="save" class="primary" disabled>변환 후 저장 <span aria-hidden="true">↗</span></button></div></div>
|
|
27
63
|
<div class="feedback"><div id="status" class="status" role="status" aria-live="polite"></div><div id="result" class="result" hidden><div><strong>변환 결과 저장 완료</strong><p id="output"></p></div><button id="open">결과 폴더 열기 ↗</button></div><details id="details" hidden><summary>변환 내역</summary><pre id="log"></pre></details></div>
|
|
28
64
|
</section>
|
|
29
65
|
<section class="panel comparison" aria-label="본문 비교">
|
|
30
|
-
<div class="panel-heading comparison-heading"><h2
|
|
66
|
+
<div class="panel-heading comparison-heading"><h2>본문 비교</h2><div class="diff-toolbar"><span id="preview-label" class="count">미리보기 대기</span><span class="toolbar-divider"></span><label class="layout-toggle"><input id="side-by-side" type="checkbox" checked>나란히</label><div class="diff-navigation"><button id="previous-change" class="quiet" aria-label="이전 변경" title="이전 변경" disabled>↑</button><button id="next-change" class="quiet" aria-label="다음 변경" title="다음 변경" disabled>↓</button></div></div></div>
|
|
31
67
|
<div id="preview-documents" class="preview-documents" hidden><label for="preview-document">비교할 문서</label><select id="preview-document"></select><span id="preview-count" class="hint"></span></div><div class="diff-labels"><span><i class="legend-before"></i>원본</span><span><i class="legend-after"></i>변환 결과</span><span class="read-only">읽기 전용</span></div>
|
|
32
68
|
<div id="editor-error" role="alert" hidden></div><div class="editor-shell"><div id="diff" class="diff" aria-label="Monaco 본문 비교"></div><div id="comparison-empty" class="comparison-empty"><span class="empty-symbol" aria-hidden="true">⇄</span><strong>변경된 부분을 한눈에</strong><p>문서를 선택하고 미리보기를 누르면<br>원본과 변환 결과를 비교할 수 있습니다.</p><div class="empty-legend"><span><i class="legend-before"></i>삭제</span><span><i class="legend-after"></i>추가</span></div></div></div>
|
|
33
69
|
</section>
|
|
@@ -35,5 +71,39 @@ export const webPage = (token) => `<!doctype html>
|
|
|
35
71
|
${syncPanel}
|
|
36
72
|
</div>
|
|
37
73
|
<footer><span id="workspace-note"><i></i>변환은 이 컴퓨터에서 처리됩니다.</span><span>Markdown · Confluence</span></footer>
|
|
74
|
+
<dialog id="local-connect-dialog" class="local-connect-dialog" aria-labelledby="local-connect-title">
|
|
75
|
+
<h2 id="local-connect-title">내 컴퓨터의 csync 연결</h2>
|
|
76
|
+
<p>이 웹은 화면만 제공합니다. 로컬 csync가 문서·첨부·연결 정보를 처리합니다.</p>
|
|
77
|
+
<ol><li>내 컴퓨터에서 아래 명령을 실행하세요.</li><li>터미널에 표시된 연결 링크를 이 브라우저에서 여세요.</li><li>로컬 네트워크 접근 요청이 나오면 허용하고 연결을 누르세요.</li></ol>
|
|
78
|
+
<pre id="local-connect-command">csync web --ui https://배포주소 --base /문서폴더</pre>
|
|
79
|
+
<form id="local-connect-form">
|
|
80
|
+
<label for="local-connect-endpoint">로컬 실행기 주소</label><input id="local-connect-endpoint" type="url" value="http://127.0.0.1:4318" required autocomplete="off">
|
|
81
|
+
<label for="local-connect-token">연결 키</label><input id="local-connect-token" type="password" required autocomplete="off" spellcheck="false">
|
|
82
|
+
<p>연결한 웹에 로컬 csync의 파일 작업 권한을 부여합니다. 신뢰하는 웹만 연결하세요. 연결 키는 이 탭의 세션에만 보관하며 csync를 종료하면 만료됩니다.</p>
|
|
83
|
+
<p id="local-connect-note" role="status" aria-live="polite"></p><button id="local-connect-submit" class="primary" type="submit">내 컴퓨터에 연결</button>
|
|
84
|
+
</form>
|
|
85
|
+
</dialog>
|
|
86
|
+
<dialog id="browser-folder-dialog" class="local-connect-dialog" aria-labelledby="browser-folder-title">
|
|
87
|
+
<h2 id="browser-folder-title">내 컴퓨터의 문서 폴더 열기</h2>
|
|
88
|
+
<p>로컬 프로그램 설치 없이 브라우저에서 문서 폴더를 선택하세요. 읽기·쓰기 권한을 허용하면 결과를 같은 폴더에 반영할 수 있습니다.</p>
|
|
89
|
+
<p>${runtime === 'standalone' ? '변환과 변경 확인은 브라우저에서 처리합니다. Confluence 통신에 필요한 인증과 업로드 대상 문서·첨부만 중계 API로 전달합니다. 큰 첨부는 전송 동안 비공개 임시 저장소를 거칩니다. 작업 중에는 이 탭을 열어 두세요.' : '선택한 문서·출력·참조 폴더의 파일과 .env를 Docker 서버의 세션별 임시 작업공간으로 전달해 처리합니다. 출력은 전용 결과 폴더를 선택하세요. 작업공간은 연결 해제 또는 유휴 만료 시 정리합니다.'}</p>
|
|
90
|
+
<p>데스크톱 Chrome·Edge와 HTTPS가 필요합니다. 파일별 100MiB, 전체 512MiB까지 처리합니다.</p>
|
|
91
|
+
<p id="browser-folder-status" role="status" aria-live="polite"></p>
|
|
92
|
+
<button id="browser-folder-resume" class="primary" type="button" hidden>이전 폴더 다시 연결</button>
|
|
93
|
+
<button id="browser-folder-open" class="primary" type="button">문서 폴더 열기</button>
|
|
94
|
+
</dialog>
|
|
95
|
+
<dialog id="browser-apply-dialog" class="local-connect-dialog" aria-labelledby="browser-apply-title">
|
|
96
|
+
<h2 id="browser-apply-title">로컬 파일 적용 확인</h2><p id="browser-apply-status" role="alert"></p>
|
|
97
|
+
<p>처리 결과는 아직 보관 중입니다. 로컬 수정 사항을 다른 곳에 보관하거나 충돌을 해결한 뒤 다시 시도하세요. 이 창을 닫거나 새 작업으로 덮어쓰지 않습니다.</p>
|
|
98
|
+
<button id="browser-apply-retry" class="primary" type="button">다시 적용 확인</button>
|
|
99
|
+
</dialog>
|
|
100
|
+
<dialog id="overwrite-dialog" class="env-confirm-dialog overwrite-dialog" aria-labelledby="overwrite-title" aria-describedby="overwrite-warning">
|
|
101
|
+
<h3 id="overwrite-title">파일 덮어쓰기 확인</h3>
|
|
102
|
+
<p class="hint">저장 위치</p><p id="overwrite-path" class="overwrite-path"></p>
|
|
103
|
+
<p id="overwrite-warning" class="restore-warning"></p>
|
|
104
|
+
<ul id="overwrite-files" class="overwrite-files" aria-label="덮어쓸 파일"></ul>
|
|
105
|
+
<div class="actions"><button id="overwrite-cancel" type="button" autofocus>취소</button><button id="overwrite-confirm" type="button" class="primary">덮어쓰기</button></div>
|
|
106
|
+
</dialog>
|
|
38
107
|
${helpDialog}
|
|
39
|
-
|
|
108
|
+
<dialog id="table-review-dialog" class="table-review-dialog" aria-labelledby="table-review-title"><div class="panel-heading"><h2 id="table-review-title">문서 서식 변경 확인</h2><button id="table-review-close">닫기</button></div><p id="format-review-note" class="table-review-note">기본 서식과 표 조절을 함께 확인한 뒤 저장하세요.</p><div id="table-review-diff" class="table-review-diff"></div><p id="table-save-status" role="status" aria-live="polite"></p><div class="table-review-footer"><button id="table-save" class="primary">원본 파일에 저장</button></div></dialog>
|
|
109
|
+
</main><aside id="activity-panel" class="activity-panel" role="status" aria-live="polite" aria-atomic="false" hidden><span class="activity-spinner" aria-hidden="true"></span><div class="activity-content"><strong id="activity-title"></strong><p id="activity-detail"></p><div class="activity-meta"><time id="activity-elapsed" aria-hidden="true"></time><a id="activity-link" href="#sync-results" hidden>실행 내역 보기 ↓</a></div></div></aside><aside id="snackbars" class="snackbars" aria-label="작업 알림"></aside><script src="/assets/app.js" type="module"></script></body></html>`;
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/** Transport-independent workspace routes, shared by the Node server and browser worker. */
|
|
2
|
+
import { randomBytes, createHash } from 'node:crypto';
|
|
3
|
+
import { readdir, realpath, stat, lstat, readFile, mkdtemp, rm } from 'node:fs/promises';
|
|
4
|
+
import { resolve, dirname, basename, join, relative } from 'node:path';
|
|
5
|
+
import { homedir, tmpdir } from 'node:os';
|
|
6
|
+
import { collectMarkdown } from '../documents/catalog.js';
|
|
7
|
+
import { createWebSync } from './sync.js';
|
|
8
|
+
import { previewDocument } from './preview.js';
|
|
9
|
+
import { createTableFormatting } from './table-format.js';
|
|
10
|
+
import { within, canonical, publishOutput, OverwriteRequired } from './output.js';
|
|
11
|
+
const message = (error) => error instanceof Error ? error.message : String(error);
|
|
12
|
+
export function createWorkspaceApi(options) {
|
|
13
|
+
const start = options.start, previewNonce = options.previewNonce ?? randomBytes(24).toString('hex');
|
|
14
|
+
const tableFormatting = createTableFormatting();
|
|
15
|
+
const sync = createWebSync(options.syncRunner, options.connectionImports);
|
|
16
|
+
const results = new Set();
|
|
17
|
+
let busy = null, picking = false;
|
|
18
|
+
const conflictMessage = () => {
|
|
19
|
+
if (busy)
|
|
20
|
+
return `${busy} 작업이 진행 중입니다. 완료 후 다시 시도해 주세요.`;
|
|
21
|
+
const job = sync.current();
|
|
22
|
+
const name = job?.state === 'running' ? { restore: '문서 복구', pull: '가져오기(Pull)', push: '업로드(Push)', preview: '업로드 미리보기', fetch: '원격 변경 확인' }[job.kind] : 'Confluence 요청 처리';
|
|
23
|
+
return `${name} 작업이 진행 중입니다. 완료 후 다시 시도해 주세요.`;
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
connectionImports: sync.connectionImports,
|
|
27
|
+
isRunning: () => Boolean(busy || sync.running()),
|
|
28
|
+
async request(method, url, body) {
|
|
29
|
+
let response = { status: 404, body: { error: '없는 요청입니다.' } };
|
|
30
|
+
const json = (status, body) => { response = { status, body }; };
|
|
31
|
+
try {
|
|
32
|
+
await (async () => {
|
|
33
|
+
if (method === 'GET' && url.pathname === '/api/sync/job') {
|
|
34
|
+
json(200, { job: sync.current() });
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (method === 'GET' && url.pathname === '/api/browse') {
|
|
38
|
+
const path = await realpath(url.searchParams.get('path') || start);
|
|
39
|
+
const entries = (await readdir(path, { withFileTypes: true }))
|
|
40
|
+
.filter(e => !e.name.startsWith('.') && e.name !== 'node_modules' && !(e.isDirectory() && e.name.toLowerCase() === 'attachments') && (e.isDirectory() || (e.isFile() && /\.md$/i.test(e.name))))
|
|
41
|
+
.map(e => ({ name: e.name, path: join(path, e.name), directory: e.isDirectory() }))
|
|
42
|
+
.sort((a, b) => Number(b.directory) - Number(a.directory) || a.name.localeCompare(b.name, 'ko'));
|
|
43
|
+
json(200, { path, parent: dirname(path), home: homedir(), entries });
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (method === 'GET' && url.pathname === '/api/search') {
|
|
47
|
+
const root = await realpath(url.searchParams.get('path') || start);
|
|
48
|
+
const query = (url.searchParams.get('q') || '').trim().normalize('NFC').toLocaleLowerCase();
|
|
49
|
+
if (!(await stat(root)).isDirectory())
|
|
50
|
+
throw new Error('검색 기준 폴더를 확인해 주세요.');
|
|
51
|
+
const entries = [];
|
|
52
|
+
let skipped = 0;
|
|
53
|
+
const pending = query ? [root] : [];
|
|
54
|
+
while (pending.length) {
|
|
55
|
+
const dir = pending.pop();
|
|
56
|
+
let children;
|
|
57
|
+
try {
|
|
58
|
+
children = await readdir(dir, { withFileTypes: true });
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (dir === root)
|
|
62
|
+
throw error;
|
|
63
|
+
skipped++;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
for (const entry of children) {
|
|
67
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules' || (entry.isDirectory() && entry.name.toLowerCase() === 'attachments'))
|
|
68
|
+
continue;
|
|
69
|
+
const path = join(dir, entry.name);
|
|
70
|
+
// Do not follow symlinks: avoid cycles and paths outside the selected tree.
|
|
71
|
+
if (entry.isDirectory())
|
|
72
|
+
pending.push(path);
|
|
73
|
+
else if (entry.isFile() && /\.md$/i.test(entry.name)
|
|
74
|
+
&& entry.name.normalize('NFC').toLocaleLowerCase().includes(query)) {
|
|
75
|
+
entries.push({ name: entry.name, path, relativePath: relative(root, path).split('\\').join('/'), directory: false });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
entries.sort((a, b) => a.relativePath.localeCompare(b.relativePath, 'ko'));
|
|
80
|
+
json(200, { root, entries, skipped });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (method !== 'POST' || !['/api/document-format/preview', '/api/table-format/preview', '/api/table-format/save', '/api/document-preview', '/api/convert', '/api/open', '/api/pick-folder', '/api/sync/diff', '/api/sync/staging', '/api/sync/config', '/api/sync/env/import', '/api/sync/env/save', '/api/sync/connect', '/api/sync/preview', '/api/sync/push', '/api/sync/pull', '/api/sync/fetch', '/api/sync/restore'].includes(url.pathname)) {
|
|
84
|
+
json(404, { error: '없는 요청입니다.' });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
88
|
+
throw new Error('요청 내용을 확인해 주세요.');
|
|
89
|
+
if (url.pathname === '/api/document-preview') {
|
|
90
|
+
if (typeof body.base !== 'string' || typeof body.file !== 'string')
|
|
91
|
+
throw new Error('문서 경로를 확인해 주세요.');
|
|
92
|
+
json(200, await previewDocument(body.base, body.file, previewNonce));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (url.pathname === '/api/document-format/preview') {
|
|
96
|
+
if (busy || sync.running()) {
|
|
97
|
+
json(409, { error: conflictMessage() });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (typeof body.base !== 'string' || typeof body.file !== 'string' || typeof body.hash !== 'string')
|
|
101
|
+
throw new Error('문서 서식 요청을 확인해 주세요.');
|
|
102
|
+
json(200, await tableFormatting.documentPreview(body.base, body.file, body.hash, body.basic, body.tables, body.changes));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (url.pathname.startsWith('/api/table-format/')) {
|
|
106
|
+
if (busy || sync.running()) {
|
|
107
|
+
json(409, { error: conflictMessage() });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (url.pathname.endsWith('/preview')) {
|
|
111
|
+
if (typeof body.base !== 'string' || typeof body.file !== 'string' || typeof body.hash !== 'string'
|
|
112
|
+
|| !Number.isInteger(body.index) || !Array.isArray(body.widths))
|
|
113
|
+
throw new Error('표 서식 요청을 확인해 주세요.');
|
|
114
|
+
json(200, await tableFormatting.preview(body.base, body.file, body.hash, body.index, body.widths, body.changes));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
if (typeof body.planId !== 'string')
|
|
118
|
+
throw new Error('변경 비교를 먼저 실행해 주세요.');
|
|
119
|
+
busy = '표 서식 저장';
|
|
120
|
+
try {
|
|
121
|
+
json(200, await tableFormatting.save(body.planId));
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
busy = null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (url.pathname === '/api/sync/connect') {
|
|
130
|
+
if (busy || sync.running()) {
|
|
131
|
+
json(409, { error: conflictMessage() });
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
busy = '스페이스 연결 저장';
|
|
135
|
+
try {
|
|
136
|
+
json(200, await sync.connect(body));
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
busy = null;
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (url.pathname === '/api/sync/env/import' || url.pathname === '/api/sync/env/save') {
|
|
144
|
+
if (busy || sync.running()) {
|
|
145
|
+
json(409, { error: conflictMessage() });
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
busy = url.pathname.endsWith('/import') ? '.env 가져오기' : '.env 저장';
|
|
149
|
+
try {
|
|
150
|
+
json(200, url.pathname.endsWith('/import') ? await sync.importEnv(body) : await sync.saveEnv(body));
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
busy = null;
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (url.pathname === '/api/sync/diff') {
|
|
158
|
+
if (busy || sync.running()) {
|
|
159
|
+
json(409, { error: conflictMessage() });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
json(200, await sync.diff(body));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (url.pathname === '/api/sync/restore') {
|
|
166
|
+
if (busy || sync.running()) {
|
|
167
|
+
json(409, { error: conflictMessage() });
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const result = await sync.restore(body);
|
|
171
|
+
json('job' in result ? 202 : 200, result);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (url.pathname === '/api/sync/staging') {
|
|
175
|
+
if (busy || sync.running()) {
|
|
176
|
+
json(409, { error: conflictMessage() });
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
json(200, await sync.staging(body));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (url.pathname === '/api/sync/config') {
|
|
183
|
+
if (typeof body.base !== 'string' || typeof body.envId !== 'string')
|
|
184
|
+
throw new Error('설정 경로를 확인해 주세요.');
|
|
185
|
+
json(200, await sync.config(body.base, body.envId));
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (url.pathname === '/api/sync/preview' || url.pathname === '/api/sync/push' || url.pathname === '/api/sync/pull' || url.pathname === '/api/sync/fetch') {
|
|
189
|
+
if (busy || sync.running()) {
|
|
190
|
+
json(409, { error: conflictMessage() });
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const job = url.pathname.endsWith('/preview') ? await sync.preview(body) : url.pathname.endsWith('/pull') ? await sync.pull(body) : url.pathname.endsWith('/fetch') ? await sync.fetch(body) : await sync.push(body.planId);
|
|
194
|
+
json(202, { job });
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (url.pathname === '/api/pick-folder') {
|
|
198
|
+
if (picking) {
|
|
199
|
+
json(409, { error: '이미 열린 폴더 다이얼로그를 먼저 닫아 주세요.' });
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (body.kind !== 'base' && body.kind !== 'out' && body.kind !== 'reference')
|
|
203
|
+
throw new Error('폴더 선택 용도를 확인해 주세요.');
|
|
204
|
+
let initial = start;
|
|
205
|
+
if (typeof body.start === 'string' && body.start) {
|
|
206
|
+
try {
|
|
207
|
+
const path = await realpath(body.start);
|
|
208
|
+
if ((await stat(path)).isDirectory())
|
|
209
|
+
initial = path;
|
|
210
|
+
}
|
|
211
|
+
catch { /* Use initial folder for new paths. */ }
|
|
212
|
+
}
|
|
213
|
+
if (picking) {
|
|
214
|
+
json(409, { error: '이미 열린 폴더 다이얼로그를 먼저 닫아 주세요.' });
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
picking = true;
|
|
218
|
+
try {
|
|
219
|
+
const selected = await options.pickFolder(initial, body.kind === 'reference' ? '참조 문서의 동기화 폴더 선택' : body.kind === 'base' ? '문서의 기준 폴더 선택' : '변환 결과를 저장할 출력 폴더 선택');
|
|
220
|
+
const path = selected ? await realpath(selected) : null;
|
|
221
|
+
if (path && !(await stat(path)).isDirectory())
|
|
222
|
+
throw new Error('폴더를 선택해 주세요.');
|
|
223
|
+
json(200, { path });
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
picking = false;
|
|
227
|
+
}
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (url.pathname === '/api/open') {
|
|
231
|
+
if (typeof body.path !== 'string' || !results.has(body.path))
|
|
232
|
+
throw new Error('이번 실행에서 저장한 결과만 열 수 있습니다.');
|
|
233
|
+
await options.open(body.path);
|
|
234
|
+
json(200, { ok: true });
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (busy || sync.running()) {
|
|
238
|
+
json(409, { error: conflictMessage() });
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (typeof body.base !== 'string' || typeof body.file !== 'string'
|
|
242
|
+
|| !['markdown', 'obsidian', 'repair'].includes(body.to)
|
|
243
|
+
|| typeof body.fix !== 'boolean' || typeof body.preview !== 'boolean')
|
|
244
|
+
throw new Error('변환 옵션을 확인해 주세요.');
|
|
245
|
+
const browseBase = await realpath(body.base);
|
|
246
|
+
const file = await realpath(body.file);
|
|
247
|
+
const scope = body.scope ?? 'legacy';
|
|
248
|
+
if (!['file', 'folder', 'legacy'].includes(scope))
|
|
249
|
+
throw new Error('파일 또는 폴더를 선택해 주세요.');
|
|
250
|
+
const info = await stat(file);
|
|
251
|
+
if (!(await stat(browseBase)).isDirectory() || !within(browseBase, file)
|
|
252
|
+
|| (scope === 'folder' ? !info.isDirectory() : !info.isFile() || !/\.md$/i.test(file))) {
|
|
253
|
+
throw new Error('탐색 폴더 안의 Markdown 파일 또는 폴더를 선택해 주세요.');
|
|
254
|
+
}
|
|
255
|
+
const components = relative(browseBase, file).split(/[\\/]/);
|
|
256
|
+
if ((scope === 'folder' ? components : components.slice(0, -1)).some(p => p.startsWith('.') || p === 'node_modules')) {
|
|
257
|
+
throw new Error('숨김 폴더와 node_modules 안의 문서는 변환 대상이 아닙니다.');
|
|
258
|
+
}
|
|
259
|
+
const base = scope === 'legacy' ? browseBase : scope === 'folder' ? file : dirname(file);
|
|
260
|
+
if (dirname(base) === base)
|
|
261
|
+
throw new Error('문서가 있는 하위 폴더를 선택해 주세요.');
|
|
262
|
+
const documents = scope === 'folder' ? collectMarkdown(file) : [file];
|
|
263
|
+
if (!documents.length)
|
|
264
|
+
throw new Error('선택한 폴더에 변환할 Markdown 문서가 없습니다.');
|
|
265
|
+
if (body.out !== undefined && typeof body.out !== 'string')
|
|
266
|
+
throw new Error('출력 경로를 확인해 주세요.');
|
|
267
|
+
const approved = body.approvedOverwrites ?? {};
|
|
268
|
+
if (!approved || typeof approved !== 'object' || Array.isArray(approved)
|
|
269
|
+
|| Object.values(approved).some(value => typeof value !== 'string' || !/^[a-f0-9]{64}$/.test(value)))
|
|
270
|
+
throw new Error('덮어쓰기 확인 정보를 확인해 주세요.');
|
|
271
|
+
const requestedOut = body.out?.trim() ? await canonical(resolve(body.out.trim())) : null;
|
|
272
|
+
const inPlace = requestedOut !== null && (requestedOut === browseBase || requestedOut === base);
|
|
273
|
+
const effectiveOut = inPlace ? base : requestedOut;
|
|
274
|
+
if (requestedOut && !inPlace && (within(base, requestedOut) || within(requestedOut, base))) {
|
|
275
|
+
throw new Error('출력 폴더는 기준 폴더와 겹치지 않는 별도 경로로 지정해 주세요.');
|
|
276
|
+
}
|
|
277
|
+
// Validation above awaits filesystem reads; another request may have acquired the slot.
|
|
278
|
+
if (busy || sync.running()) {
|
|
279
|
+
json(409, { error: conflictMessage() });
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
busy = '문서 변환';
|
|
283
|
+
let out;
|
|
284
|
+
let destination = null;
|
|
285
|
+
let generatedDestination = false;
|
|
286
|
+
let response;
|
|
287
|
+
let responseStatus = 200;
|
|
288
|
+
let saved;
|
|
289
|
+
const verifySourceApproval = async () => {
|
|
290
|
+
if (!inPlace)
|
|
291
|
+
return;
|
|
292
|
+
for (const [rel, hash] of Object.entries(approved)) {
|
|
293
|
+
const path = resolve(base, rel);
|
|
294
|
+
if (!within(base, path))
|
|
295
|
+
throw new Error('덮어쓰기 확인 경로를 확인하세요.');
|
|
296
|
+
try {
|
|
297
|
+
const info = await lstat(path);
|
|
298
|
+
if (info.isFile() && !info.isSymbolicLink() && createHash('sha256').update(await readFile(path)).digest('hex') === hash)
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
if (error.code !== 'ENOENT')
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
throw Object.assign(new Error(`확인 이후 원본 파일이 변경되었거나 삭제되었습니다: ${rel}. 미리보기부터 다시 진행하세요.`), { code: 'SOURCE_CHANGED' });
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
try {
|
|
309
|
+
await verifySourceApproval();
|
|
310
|
+
out = await mkdtemp(join(tmpdir(), 'csync-preview-'));
|
|
311
|
+
const args = ['--base', base, file, '--out', out];
|
|
312
|
+
if (body.to !== 'repair')
|
|
313
|
+
args.push('--to', body.to);
|
|
314
|
+
if (body.fix || body.to === 'repair')
|
|
315
|
+
args.push('--fix');
|
|
316
|
+
const { stdout } = await options.convert(args);
|
|
317
|
+
const readPreview = async (path) => {
|
|
318
|
+
if ((await stat(path)).size > 200_000)
|
|
319
|
+
return '문서가 200KB를 넘어 본문 미리보기를 생략합니다.';
|
|
320
|
+
return readFile(path, 'utf8');
|
|
321
|
+
};
|
|
322
|
+
const previews = await Promise.all(documents.slice(0, 20).map(async (path) => ({
|
|
323
|
+
path: relative(base, path), before: await readPreview(path), after: await readPreview(join(out, relative(base, path))),
|
|
324
|
+
})));
|
|
325
|
+
const { before, after } = previews[0];
|
|
326
|
+
if (!body.preview) {
|
|
327
|
+
await verifySourceApproval();
|
|
328
|
+
destination = effectiveOut ?? await mkdtemp(join(dirname(base), `${basename(base)}-converted-`));
|
|
329
|
+
generatedDestination = !effectiveOut;
|
|
330
|
+
saved = await publishOutput(out, destination, approved);
|
|
331
|
+
results.add(destination);
|
|
332
|
+
}
|
|
333
|
+
response = { before, after, previews, saved, inPlace, documentCount: documents.length, log: stdout.split(out).join(destination ?? '(미리보기)'), output: destination };
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
if (destination && generatedDestination)
|
|
337
|
+
await rm(destination, { recursive: true, force: true });
|
|
338
|
+
if (error.code === 'SOURCE_CHANGED') {
|
|
339
|
+
responseStatus = 409;
|
|
340
|
+
response = { error: message(error), code: 'SOURCE_CHANGED' };
|
|
341
|
+
}
|
|
342
|
+
else if (error instanceof OverwriteRequired) {
|
|
343
|
+
responseStatus = 409;
|
|
344
|
+
const stale = inPlace && error.conflicts.some(item => approved[item.path] && approved[item.path] !== item.hash);
|
|
345
|
+
response = stale ? { error: '확인 이후 원본 파일이 변경되었습니다. 미리보기부터 다시 진행하세요.', code: 'SOURCE_CHANGED' }
|
|
346
|
+
: { error: error.message, code: 'OVERWRITE_REQUIRED', conflicts: error.conflicts, inPlace, output: destination };
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
const failure = error;
|
|
350
|
+
throw new Error(failure.killed ? '변환 시간이 2분을 초과했습니다.' : failure.stderr?.trim() || message(error));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
finally {
|
|
354
|
+
try {
|
|
355
|
+
if (out)
|
|
356
|
+
await rm(out, { recursive: true, force: true });
|
|
357
|
+
}
|
|
358
|
+
finally {
|
|
359
|
+
busy = null;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
json(responseStatus, response);
|
|
363
|
+
})();
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
json(400, { error: message(error) });
|
|
367
|
+
}
|
|
368
|
+
return response;
|
|
369
|
+
},
|
|
370
|
+
};
|
|
371
|
+
}
|