@hardikg/mdview 0.6.2 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/README.md +8 -10
- package/bin/mdview.mjs +123 -44
- package/dist/client/assets/{arc-DjKPIdc5.js → arc-DYMLyL-_.js} +1 -1
- package/dist/client/assets/{architectureDiagram-3BPJPVTR-_OLcSJC-.js → architectureDiagram-3BPJPVTR-iXijJkGs.js} +1 -1
- package/dist/client/assets/{blockDiagram-GPEHLZMM-BxHKbhSj.js → blockDiagram-GPEHLZMM-Dmjj2Ajf.js} +1 -1
- package/dist/client/assets/{c4Diagram-AAUBKEIU-DiBLe7ZZ.js → c4Diagram-AAUBKEIU-kJb6eQJJ.js} +1 -1
- package/dist/client/assets/channel-DirF0LQF.js +1 -0
- package/dist/client/assets/{chunk-2J33WTMH-4B03UoyK.js → chunk-2J33WTMH-T3vdO-20.js} +1 -1
- package/dist/client/assets/{chunk-4BX2VUAB-Dw7jUHGw.js → chunk-4BX2VUAB-By-ok8D7.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-AZdo8c-x.js → chunk-55IACEB6-BDUDnCl9.js} +1 -1
- package/dist/client/assets/{chunk-727SXJPM-g0GurlcS.js → chunk-727SXJPM-Bj1JFNtT.js} +1 -1
- package/dist/client/assets/{chunk-AQP2D5EJ-eqTzq7th.js → chunk-AQP2D5EJ-BPZWXvxS.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-D4omRKrC.js → chunk-FMBD7UC4-BYGwnUQ4.js} +1 -1
- package/dist/client/assets/{chunk-ND2GUHAM-mVdFtWqM.js → chunk-ND2GUHAM-TheCoD51.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CyPMaN41.js → chunk-QZHKN3VN-Cd7CcXLC.js} +1 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-CdoMz3xi.js +1 -0
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CdoMz3xi.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-BjlwoH6Y.js → cose-bilkent-S5V4N54A-Br-CySzr.js} +1 -1
- package/dist/client/assets/{dagre-BM42HDAG-DBF5W9Tk.js → dagre-BM42HDAG-DcIxclFu.js} +1 -1
- package/dist/client/assets/{diagram-2AECGRRQ-D9gwS4tw.js → diagram-2AECGRRQ-C0xPdtMR.js} +1 -1
- package/dist/client/assets/{diagram-5GNKFQAL-DTpIgGjI.js → diagram-5GNKFQAL-B9i7DtoQ.js} +1 -1
- package/dist/client/assets/{diagram-KO2AKTUF-ByLH9Z83.js → diagram-KO2AKTUF-mwoLS2TK.js} +1 -1
- package/dist/client/assets/{diagram-LMA3HP47-Dkd4KfnW.js → diagram-LMA3HP47-4o7ohGjJ.js} +1 -1
- package/dist/client/assets/{diagram-OG6HWLK6-C-mJW1Tv.js → diagram-OG6HWLK6-C6rhUBe2.js} +1 -1
- package/dist/client/assets/{erDiagram-TEJ5UH35-DmyZPB5P.js → erDiagram-TEJ5UH35-smNcu7Lp.js} +1 -1
- package/dist/client/assets/{flowDiagram-I6XJVG4X-D6RrsDb6.js → flowDiagram-I6XJVG4X-CPuYTxJr.js} +1 -1
- package/dist/client/assets/{ganttDiagram-6RSMTGT7-CP_O3OdU.js → ganttDiagram-6RSMTGT7-xaV0Djok.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-PVQCEYII-DZgdWY-6.js → gitGraphDiagram-PVQCEYII-eN40bRnz.js} +1 -1
- package/dist/client/assets/index-Cmcberpw.css +1 -0
- package/dist/client/assets/index-OFdfWE8x.js +2 -0
- package/dist/client/assets/{infoDiagram-5YYISTIA-BAp5orio.js → infoDiagram-5YYISTIA-xm1BEpfJ.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-DgqFJxLz.js → ishikawaDiagram-YF4QCWOH-BDIzsJJZ.js} +1 -1
- package/dist/client/assets/{journeyDiagram-JHISSGLW-BFlCgna7.js → journeyDiagram-JHISSGLW-BfWwpj1w.js} +1 -1
- package/dist/client/assets/{kanban-definition-UN3LZRKU-Ck0sh2an.js → kanban-definition-UN3LZRKU-Cuij1uoR.js} +1 -1
- package/dist/client/assets/{linear-CfOau-eX.js → linear-CZOgGL8m.js} +1 -1
- package/dist/client/assets/{mermaid.core-BUwfPdjt.js → mermaid.core-B1i-ZGkv.js} +70 -69
- package/dist/client/assets/{mindmap-definition-RKZ34NQL-tpaiyCEI.js → mindmap-definition-RKZ34NQL-BbNc5LRE.js} +1 -1
- package/dist/client/assets/{pieDiagram-4H26LBE5-DaNIMGBH.js → pieDiagram-4H26LBE5-oB2YhWwR.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-W4KKPZXB-BUZRyB7p.js → quadrantDiagram-W4KKPZXB-CmJGRE1L.js} +1 -1
- package/dist/client/assets/{requirementDiagram-4Y6WPE33-D70DEVyJ.js → requirementDiagram-4Y6WPE33-Dp-nBLhC.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DckSl4S7.js → sankeyDiagram-5OEKKPKP-DB5dqk7n.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-3UESZ5HK-If3g75AG.js → sequenceDiagram-3UESZ5HK-jmqrpzCx.js} +1 -1
- package/dist/client/assets/{stateDiagram-AJRCARHV-C-83rolJ.js → stateDiagram-AJRCARHV-U6m2A9Wk.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-00c0MW6J.js +1 -0
- package/dist/client/assets/{timeline-definition-PNZ67QCA-DIFKx0ow.js → timeline-definition-PNZ67QCA-uVQ-oiv8.js} +1 -1
- package/dist/client/assets/{vennDiagram-CIIHVFJN-BGLU5r80.js → vennDiagram-CIIHVFJN-By-tXl41.js} +1 -1
- package/dist/client/assets/{wardley-L42UT6IY-qocA9kr0.js → wardley-L42UT6IY-ByB1C5d8.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YWT4CUSO-CWhmOVum.js → wardleyDiagram-YWT4CUSO-BQMj40Ao.js} +1 -1
- package/dist/client/assets/{xychartDiagram-2RQKCTM6-D2Sc4Taz.js → xychartDiagram-2RQKCTM6-7czjL7-G.js} +1 -1
- package/dist/client/index.html +2 -2
- package/package.json +1 -1
- package/dist/client/assets/channel-oDRyuYVi.js +0 -1
- package/dist/client/assets/classDiagram-4FO5ZUOK-Dn5QR1VE.js +0 -1
- package/dist/client/assets/classDiagram-v2-Q7XG4LA2-Dn5QR1VE.js +0 -1
- package/dist/client/assets/index-DFYTyxkU.js +0 -2
- package/dist/client/assets/index-_GInkOX2.css +0 -1
- package/dist/client/assets/stateDiagram-v2-BHNVJYJU-BeI0-mGo.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.1] — 2026-07-01
|
|
11
|
+
|
|
12
|
+
### Security
|
|
13
|
+
- Dependency security bumps applied via `npm audit fix` (lockfile-only, SemVer-compatible — no `package.json` range changes): `markdown-it`, `dompurify`, `linkify-it`, `js-yaml`, `ws`, and dev tooling. Clears all shipped-dep advisories (moderate/high DoS & complexity issues in the render path). One `low`, dev-only `esbuild` advisory (reachable solely through `tsup`'s Windows dev server, never in the published package) has no non-breaking fix and is knowingly deferred; the quality gate now audits shipped deps only (`npm audit --omit=dev`).
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Under `--vscode`, the HTML response now **omits** the `frame-ancestors` CSP directive entirely (previously `frame-ancestors *`). Chromium 142 (shipped in VS Code 1.126) narrowed `frame-ancestors *` to match only network-scheme origins (`http`/`https`/`ws`/`wss`), which excludes the webview's non-network `vscode-webview:` parent — so `*` blocked the extension from framing the preview at all. No CSP value can name the dynamic webview origin, so the directive is dropped in embed mode; the loopback bind (`127.0.0.1`) remains the real network boundary. Default browser runs still emit `frame-ancestors 'none'` byte-for-byte.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- **Narrow-width layout.** The file-tree and outline panes now collapse into overlay drawers at narrow viewport widths instead of leaving blank, full-width grid columns that squeezed the document into an unreadable vertical sliver. Root cause: the pane-width CSS variables are set inline on `.app-shell` (needed for the drag-resizers), and inline styles overrode the responsive `@media` zeroing; the breakpoints now win via `!important`. Affects the plain browser CLI and the VS Code webview alike.
|
|
20
|
+
- **Embed mode** no longer reserves a blank left column for the (never-rendered) file tree, and the outline now opens correctly in the narrow VS Code preview instead of appearing empty.
|
|
21
|
+
|
|
22
|
+
## [0.7.0] — 2026-05-28
|
|
23
|
+
|
|
24
|
+
CLI prereqs for an upcoming VS Code extension. New, additive surface — browser-mode runs are unchanged.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- **`--vscode` flag** — opts the CLI into "embedded in VS Code" mode. Bundles four behaviors so the future extension has a single, named entry point: (1) relaxes the HTML response's `frame-ancestors` CSP directive (see **Security** below); (2) emits a structured one-line JSON ready signal on stdout (`{"event":"ready","url":"…","port":…}`) instead of the legacy two human lines; (3) implies `--no-open` (the extension owns the URL); (4) tells the SPA it is embedded so it can swap behaviors via the `?embed=vscode` query. Default browser invocations are unaffected; the flag must be passed explicitly.
|
|
28
|
+
- **`--palette <name>`** — override the palette for a single run without touching `.mdview.json`. Accepts `classic | paper | nord | solarized | high-contrast`. Invalid values are rejected at parse time with an explicit error listing the allowed set.
|
|
29
|
+
- **`--port 0`** — kernel-assigned ephemeral port. Previously the parser refused any port ≤ 0; now `0` is permitted and the actual bound port is read from `app.server.address()` after listen. Under `--vscode` the bound port is reported in the ready-signal JSON. Useful when the parent process (extension, CI) needs a free port without coordinating with mdview about ranges.
|
|
30
|
+
- **`data-source-line` on every rendered heading** (`<h1>` through `<h6>`) — 0-based file-line number including any frontmatter that was stripped. Enables future editor↔preview cursor sync. Additive attribute; harmless if unused.
|
|
31
|
+
- **SPA embed-mode** — when the SPA loads with `?embed=vscode` *and* is running inside an iframe (`window.parent !== window`), it activates a handshake-driven postMessage protocol with its parent (origin-validated, one-shot `parentOrigin` capture, message-type allow-list), hides the left-hand file-tree pane (VS Code's Explorer already provides one), and routes internal-link clicks and outline-heading clicks back to the parent via `mdview/internal-link-clicked` / `mdview/heading-clicked` messages instead of navigating in-page. The `?embed=vscode` query is preserved across all SPA-internal navigation.
|
|
32
|
+
|
|
33
|
+
### Security
|
|
34
|
+
- **`frame-ancestors` relaxed to `*` *only* when `--vscode` is passed.** Default browser runs still emit `frame-ancestors 'none'` byte-for-byte. All other CSP directives (`default-src 'self'`, `script-src 'self'`, `style-src 'self' 'unsafe-inline'`, `img-src 'self' data: blob:`, `font-src 'self' data:`, `connect-src 'self'`, `base-uri 'none'`, `form-action 'none'`) are unchanged in both modes — a regression test in `tests/server/embed-mode.test.ts` enforces this. Justification: the loopback bind (`127.0.0.1`) is the real network-layer boundary; `frame-ancestors` is defense-in-depth on top. Enumerating VS Code webview origins (which vary across desktop, web, and Codespaces) is brittle and not portable; `frame-ancestors *` gated by the explicit `--vscode` flag is the conservative middle ground.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
- Under `--vscode`, stdout emits a single line of JSON (`{"event":"ready",…}`) rather than the legacy `mdview → <url>` and `watching: <root>` human lines. Default mode is unchanged.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
- `npm install -g` no longer silently no-ops on certain platforms. The new "only run `main()` when invoked as the entry point" guard now resolves symlinks (via `realpathSync`) on both sides of the comparison, so a globally-installed mdview (which lives behind a symlink in the global bin directory) still runs as expected.
|
|
41
|
+
- `data-source-line` line counts are robust across CRLF-authored sources and frontmatter blocks with trailing blank lines — computed directly from `raw` via a line-walk rather than relying on `gray-matter`'s internal byte representation.
|
|
42
|
+
|
|
10
43
|
## [0.6.2] — 2026-05-19
|
|
11
44
|
|
|
12
45
|
### Fixed
|
package/README.md
CHANGED
|
@@ -22,6 +22,14 @@ Reading long markdown docs is hard:
|
|
|
22
22
|
|
|
23
23
|
`mdview` is built for that. It's a single-machine, single-user, read-only viewer with editorial typography, persistent navigation, and live reload.
|
|
24
24
|
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g @hardikg/mdview
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Requires Node >= 20. The `mdview` binary lands on your `PATH`.
|
|
32
|
+
|
|
25
33
|
## Install from source
|
|
26
34
|
|
|
27
35
|
```bash
|
|
@@ -51,16 +59,6 @@ Send them the `.tgz` (Slack/Drive/AirDrop). They install with:
|
|
|
51
59
|
npm install -g ./mdview-0.6.0.tgz
|
|
52
60
|
```
|
|
53
61
|
|
|
54
|
-
Or skip the tarball entirely:
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
npm install -g github:hardikg2907/mdview
|
|
58
|
-
# or one-shot
|
|
59
|
-
npx github:hardikg2907/mdview ./docs
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
The `prepare` script auto-builds on their machine if `bin/` is absent.
|
|
63
|
-
|
|
64
62
|
## Usage
|
|
65
63
|
|
|
66
64
|
```bash
|
package/bin/mdview.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import { existsSync, readFileSync, statSync } from "fs";
|
|
4
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "fs";
|
|
5
5
|
import path11 from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import openBrowser from "open";
|
|
@@ -430,18 +430,38 @@ import { readFile as readFile3, stat as stat2 } from "fs/promises";
|
|
|
430
430
|
import matter from "gray-matter";
|
|
431
431
|
function parseFrontmatter(raw) {
|
|
432
432
|
if (!raw.startsWith("---")) {
|
|
433
|
-
return { data: null, body: raw };
|
|
433
|
+
return { data: null, body: raw, bodyStartLine: 0 };
|
|
434
434
|
}
|
|
435
435
|
try {
|
|
436
436
|
const parsed = matter(raw);
|
|
437
|
+
const body = parsed.content.replace(/^(?:\r?\n)+/, "");
|
|
438
|
+
const bodyStartLine = computeBodyStartLine(raw);
|
|
437
439
|
return {
|
|
438
440
|
data: parsed.data ?? null,
|
|
439
|
-
body
|
|
441
|
+
body,
|
|
442
|
+
bodyStartLine
|
|
440
443
|
};
|
|
441
444
|
} catch {
|
|
442
|
-
return { data: null, body: raw };
|
|
445
|
+
return { data: null, body: raw, bodyStartLine: 0 };
|
|
443
446
|
}
|
|
444
447
|
}
|
|
448
|
+
function computeBodyStartLine(raw) {
|
|
449
|
+
const lines = raw.split(/\r?\n/);
|
|
450
|
+
if (lines[0] !== "---") return 0;
|
|
451
|
+
let closingIdx = -1;
|
|
452
|
+
for (let i = 1; i < lines.length; i++) {
|
|
453
|
+
if (lines[i] === "---") {
|
|
454
|
+
closingIdx = i;
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (closingIdx < 0) return 0;
|
|
459
|
+
let bodyLine = closingIdx + 1;
|
|
460
|
+
while (bodyLine < lines.length && lines[bodyLine] === "") {
|
|
461
|
+
bodyLine++;
|
|
462
|
+
}
|
|
463
|
+
return bodyLine;
|
|
464
|
+
}
|
|
445
465
|
|
|
446
466
|
// src/render/links.ts
|
|
447
467
|
import path6 from "path/posix";
|
|
@@ -649,9 +669,12 @@ md.use(anchor, {
|
|
|
649
669
|
});
|
|
650
670
|
md.use(taskLists, { enabled: false, label: false });
|
|
651
671
|
md.use(mathPlugin);
|
|
652
|
-
async function renderMarkdown(source) {
|
|
672
|
+
async function renderMarkdown(source, bodyStartLine = 0) {
|
|
653
673
|
const tokens = md.parse(source, {});
|
|
654
674
|
for (const token of tokens) {
|
|
675
|
+
if (token.type === "heading_open" && token.map) {
|
|
676
|
+
token.attrSet("data-source-line", String(token.map[0] + bodyStartLine));
|
|
677
|
+
}
|
|
655
678
|
if (token.type === "fence") {
|
|
656
679
|
const lang = token.info.trim().split(/\s+/)[0] || "text";
|
|
657
680
|
if (lang === "mermaid") {
|
|
@@ -738,8 +761,8 @@ function registerApiFile(app, rootAbsPath, rootInfo) {
|
|
|
738
761
|
} catch {
|
|
739
762
|
return reply.code(404).send({ error: "File not found" });
|
|
740
763
|
}
|
|
741
|
-
const { data, body } = parseFrontmatter(raw);
|
|
742
|
-
const { html: rawHtml, tokens } = await renderMarkdown(body);
|
|
764
|
+
const { data, body, bodyStartLine } = parseFrontmatter(raw);
|
|
765
|
+
const { html: rawHtml, tokens } = await renderMarkdown(body, bodyStartLine);
|
|
743
766
|
const html = rewriteImageSrc(tagInternalLinks(rawHtml, relPath), relPath);
|
|
744
767
|
const outline = extractOutline(tokens);
|
|
745
768
|
const title = (typeof data?.title === "string" ? data.title : null) ?? outline[0]?.text ?? null;
|
|
@@ -1005,9 +1028,13 @@ function registerApiSearch(app, rootAbsPath, rootInfo, configState) {
|
|
|
1005
1028
|
|
|
1006
1029
|
// src/server/routes/api-tree.ts
|
|
1007
1030
|
import path8 from "path";
|
|
1031
|
+
function applyOverrides(config, paletteOverride) {
|
|
1032
|
+
if (!paletteOverride) return config;
|
|
1033
|
+
return { ...config ?? {}, palette: paletteOverride };
|
|
1034
|
+
}
|
|
1008
1035
|
function registerApiTree(app, rootAbsPath, rootInfo, configState) {
|
|
1009
1036
|
app.get("/api/tree", async (_req, reply) => {
|
|
1010
|
-
const config = configState.current;
|
|
1037
|
+
const config = applyOverrides(configState.current, configState.paletteOverride);
|
|
1011
1038
|
if (rootInfo.rootKind === "file") {
|
|
1012
1039
|
const single = [
|
|
1013
1040
|
{
|
|
@@ -1119,25 +1146,29 @@ function createWatcher(rootAbsPath, opts = {}) {
|
|
|
1119
1146
|
}
|
|
1120
1147
|
|
|
1121
1148
|
// src/server/index.ts
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
]
|
|
1149
|
+
function buildCspHtml(embedMode) {
|
|
1150
|
+
const directives = [
|
|
1151
|
+
"default-src 'self'",
|
|
1152
|
+
"script-src 'self'",
|
|
1153
|
+
// KaTeX + mermaid inject inline styles into rendered output; the rest of
|
|
1154
|
+
// the policy is strict.
|
|
1155
|
+
"style-src 'self' 'unsafe-inline'",
|
|
1156
|
+
"img-src 'self' data: blob:",
|
|
1157
|
+
"font-src 'self' data:",
|
|
1158
|
+
"connect-src 'self'",
|
|
1159
|
+
"base-uri 'none'",
|
|
1160
|
+
"form-action 'none'"
|
|
1161
|
+
];
|
|
1162
|
+
if (!embedMode) directives.push("frame-ancestors 'none'");
|
|
1163
|
+
return directives.join("; ");
|
|
1164
|
+
}
|
|
1135
1165
|
async function createServer(opts) {
|
|
1136
1166
|
const app = Fastify({ logger: false });
|
|
1167
|
+
const cspHtml = buildCspHtml(opts.embedMode ?? false);
|
|
1137
1168
|
app.addHook("onSend", async (_req, reply, payload) => {
|
|
1138
1169
|
const ct = String(reply.getHeader("content-type") ?? "");
|
|
1139
1170
|
if (ct.startsWith("text/html")) {
|
|
1140
|
-
reply.header("content-security-policy",
|
|
1171
|
+
reply.header("content-security-policy", cspHtml);
|
|
1141
1172
|
reply.header("x-content-type-options", "nosniff");
|
|
1142
1173
|
reply.header("referrer-policy", "no-referrer");
|
|
1143
1174
|
}
|
|
@@ -1145,7 +1176,11 @@ async function createServer(opts) {
|
|
|
1145
1176
|
});
|
|
1146
1177
|
const initialConfig = await loadEffectiveConfig(opts.rootAbsPath);
|
|
1147
1178
|
const ignoreSet = buildIgnoreSet(initialConfig?.ignore ?? []);
|
|
1148
|
-
const configState = {
|
|
1179
|
+
const configState = {
|
|
1180
|
+
current: initialConfig,
|
|
1181
|
+
ignoreSet,
|
|
1182
|
+
paletteOverride: opts.paletteOverride
|
|
1183
|
+
};
|
|
1149
1184
|
const watcher = createWatcher(opts.rootAbsPath, { ignore: ignoreSet });
|
|
1150
1185
|
const configPath = path10.join(opts.rootAbsPath, CONFIG_FILENAME);
|
|
1151
1186
|
const configWatcher = chokidar2.watch(configPath, {
|
|
@@ -1208,6 +1243,7 @@ Usage:
|
|
|
1208
1243
|
Options:
|
|
1209
1244
|
--port <n> Port to listen on (default: 7331; auto-fallback)
|
|
1210
1245
|
--no-open Don't auto-launch the browser
|
|
1246
|
+
--palette <name> Override the palette for this run (one of: ${PALETTES.join(", ")})
|
|
1211
1247
|
--version, -v Print version and exit
|
|
1212
1248
|
--help, -h Show this help
|
|
1213
1249
|
|
|
@@ -1236,7 +1272,7 @@ function readVersion() {
|
|
|
1236
1272
|
return "unknown";
|
|
1237
1273
|
}
|
|
1238
1274
|
function parseArgs(argv) {
|
|
1239
|
-
const args = { target: ".", port: 7331, portExplicit: false, open: true };
|
|
1275
|
+
const args = { target: ".", port: 7331, portExplicit: false, open: true, embedMode: false };
|
|
1240
1276
|
let targetSet = false;
|
|
1241
1277
|
for (let i = 0; i < argv.length; i++) {
|
|
1242
1278
|
const a = argv[i];
|
|
@@ -1246,19 +1282,33 @@ function parseArgs(argv) {
|
|
|
1246
1282
|
args.open = false;
|
|
1247
1283
|
continue;
|
|
1248
1284
|
}
|
|
1285
|
+
if (a === "--vscode") {
|
|
1286
|
+
args.embedMode = true;
|
|
1287
|
+
continue;
|
|
1288
|
+
}
|
|
1249
1289
|
if (a === "--port") {
|
|
1250
1290
|
const v = argv[++i];
|
|
1251
1291
|
if (!v) throw new Error("--port requires a number");
|
|
1252
1292
|
args.port = Number(v);
|
|
1253
|
-
if (!Number.isInteger(args.port) || args.port
|
|
1293
|
+
if (!Number.isInteger(args.port) || args.port < 0) throw new Error("Invalid --port");
|
|
1254
1294
|
args.portExplicit = true;
|
|
1255
1295
|
continue;
|
|
1256
1296
|
}
|
|
1297
|
+
if (a === "--palette") {
|
|
1298
|
+
const v = argv[++i];
|
|
1299
|
+
if (!v) throw new Error("--palette requires a value");
|
|
1300
|
+
if (!PALETTES.includes(v)) {
|
|
1301
|
+
throw new Error(`Invalid --palette '${v}'. Valid: ${PALETTES.join(", ")}`);
|
|
1302
|
+
}
|
|
1303
|
+
args.palette = v;
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1257
1306
|
if (a.startsWith("-")) throw new Error(`Unknown flag: ${a}`);
|
|
1258
1307
|
if (targetSet) throw new Error(`Unexpected argument: ${a}`);
|
|
1259
1308
|
args.target = a;
|
|
1260
1309
|
targetSet = true;
|
|
1261
1310
|
}
|
|
1311
|
+
if (args.embedMode) args.open = false;
|
|
1262
1312
|
return { kind: "run", args };
|
|
1263
1313
|
}
|
|
1264
1314
|
function detectRoot(target) {
|
|
@@ -1299,7 +1349,8 @@ async function listen(app, port, explicit) {
|
|
|
1299
1349
|
if (explicit) {
|
|
1300
1350
|
try {
|
|
1301
1351
|
await app.listen({ host: "127.0.0.1", port });
|
|
1302
|
-
|
|
1352
|
+
const addr = app.server.address();
|
|
1353
|
+
return typeof addr === "object" && addr !== null ? addr.port : port;
|
|
1303
1354
|
} catch (err) {
|
|
1304
1355
|
if (err.code === "EADDRINUSE") {
|
|
1305
1356
|
throw new Error(
|
|
@@ -1318,7 +1369,8 @@ Find the process with: ${portFinderHint(port)}`
|
|
|
1318
1369
|
if (attempt > 0) {
|
|
1319
1370
|
console.log(`port ${port} in use, using ${tryPort} instead`);
|
|
1320
1371
|
}
|
|
1321
|
-
|
|
1372
|
+
const addr = app.server.address();
|
|
1373
|
+
return typeof addr === "object" && addr !== null ? addr.port : tryPort;
|
|
1322
1374
|
} catch (err) {
|
|
1323
1375
|
if (err.code !== "EADDRINUSE") throw err;
|
|
1324
1376
|
}
|
|
@@ -1359,11 +1411,15 @@ async function main() {
|
|
|
1359
1411
|
console.error("Client bundle not found. Run `npm run build:client` first.");
|
|
1360
1412
|
process.exit(1);
|
|
1361
1413
|
}
|
|
1362
|
-
const app = await createServer({ rootAbsPath, rootInfo, clientDir });
|
|
1414
|
+
const app = await createServer({ rootAbsPath, rootInfo, clientDir, paletteOverride: args.palette, embedMode: args.embedMode });
|
|
1363
1415
|
const boundPort = await listen(app, args.port, args.portExplicit);
|
|
1364
1416
|
const url = rootInfo.rootKind === "file" ? `http://127.0.0.1:${boundPort}/?file=${encodeURIComponent(rootInfo.rootRelPath)}` : `http://127.0.0.1:${boundPort}/`;
|
|
1365
|
-
|
|
1366
|
-
|
|
1417
|
+
if (args.embedMode) {
|
|
1418
|
+
process.stdout.write(JSON.stringify({ event: "ready", url, port: boundPort }) + "\n");
|
|
1419
|
+
} else {
|
|
1420
|
+
console.log(`mdview \u2192 ${url}`);
|
|
1421
|
+
console.log(`watching: ${rootAbsPath}`);
|
|
1422
|
+
}
|
|
1367
1423
|
if (args.open) await openBrowser(url);
|
|
1368
1424
|
let shuttingDown = false;
|
|
1369
1425
|
const shutdown = async () => {
|
|
@@ -1382,6 +1438,15 @@ async function main() {
|
|
|
1382
1438
|
process.on("SIGINT", shutdown);
|
|
1383
1439
|
process.on("SIGTERM", shutdown);
|
|
1384
1440
|
if (process.platform !== "win32") process.on("SIGHUP", shutdown);
|
|
1441
|
+
if (args.embedMode) {
|
|
1442
|
+
process.stdin.on("end", () => {
|
|
1443
|
+
void shutdown();
|
|
1444
|
+
});
|
|
1445
|
+
process.stdin.on("error", () => {
|
|
1446
|
+
void shutdown();
|
|
1447
|
+
});
|
|
1448
|
+
process.stdin.resume();
|
|
1449
|
+
}
|
|
1385
1450
|
}
|
|
1386
1451
|
function formatError(err) {
|
|
1387
1452
|
if (!(err instanceof Error)) return null;
|
|
@@ -1400,19 +1465,33 @@ function formatError(err) {
|
|
|
1400
1465
|
}
|
|
1401
1466
|
return null;
|
|
1402
1467
|
}
|
|
1403
|
-
|
|
1404
|
-
const
|
|
1405
|
-
if (
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
console.error(`mdview: unexpected error: ${message}`);
|
|
1411
|
-
if (process.env.MDVIEW_DEBUG === "1" && err instanceof Error && err.stack) {
|
|
1412
|
-
console.error(err.stack);
|
|
1413
|
-
} else {
|
|
1414
|
-
console.error("(set MDVIEW_DEBUG=1 for full stack)");
|
|
1468
|
+
var isEntryPoint = (() => {
|
|
1469
|
+
const argv1 = process.argv[1];
|
|
1470
|
+
if (!argv1) return false;
|
|
1471
|
+
try {
|
|
1472
|
+
return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(argv1);
|
|
1473
|
+
} catch {
|
|
1474
|
+
return false;
|
|
1415
1475
|
}
|
|
1416
|
-
|
|
1417
|
-
|
|
1476
|
+
})();
|
|
1477
|
+
if (isEntryPoint) {
|
|
1478
|
+
main().catch((err) => {
|
|
1479
|
+
const known = formatError(err);
|
|
1480
|
+
if (known) {
|
|
1481
|
+
console.error(known.message);
|
|
1482
|
+
process.exit(known.code);
|
|
1483
|
+
}
|
|
1484
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1485
|
+
console.error(`mdview: unexpected error: ${message}`);
|
|
1486
|
+
if (process.env.MDVIEW_DEBUG === "1" && err instanceof Error && err.stack) {
|
|
1487
|
+
console.error(err.stack);
|
|
1488
|
+
} else {
|
|
1489
|
+
console.error("(set MDVIEW_DEBUG=1 for full stack)");
|
|
1490
|
+
}
|
|
1491
|
+
process.exit(1);
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
export {
|
|
1495
|
+
parseArgs
|
|
1496
|
+
};
|
|
1418
1497
|
//# sourceMappingURL=mdview.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a0 as ln,a1 as an,a2 as H,a3 as q,a4 as B,a5 as un,a6 as y,a7 as tn,a8 as L,a9 as _,aa as rn,ab as o,ac as on,ad as sn,ae as fn}from"./mermaid.core-
|
|
1
|
+
import{a0 as ln,a1 as an,a2 as H,a3 as q,a4 as B,a5 as un,a6 as y,a7 as tn,a8 as L,a9 as _,aa as rn,ab as o,ac as on,ad as sn,ae as fn}from"./mermaid.core-B1i-ZGkv.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,C,a){var O=I-l,i=D-h,n=C-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function W(l,h,I,D,v,A,C){var a=l-I,O=h-D,i=(C?A:-A)/L(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,F=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*L(on(0,T*T*R-P*P)),j=(P*g-m*S)/R,z=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=j-F,e=z-t,r=w-F,G=p-t;return x*x+e*e>r*r+G*G&&(j=w,z=p),{cx:j,cy:z,x01:-n,y01:-d,x11:j*(v/T-1),y11:z*(v/T-1)}}function hn(){var l=cn,h=yn,I=B(0),D=null,v=gn,A=dn,C=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,F=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(F>tn-y)a.moveTo(s*H(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*H(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=F,S=F,j=C.apply(this,arguments)/2,z=j>y&&(D?+D.apply(this,arguments):L(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(z>y){var G=sn(z/u*q(j)),M=sn(z/s*q(j));(P-=G*2)>y?(G*=t?1:-1,R+=G,T-=G):(P=0,R=T=(f+c)/2),(S-=M*2)>y?(M*=t?1:-1,m+=M,g-=M):(S=0,m=g=(f+c)/2)}var J=s*H(m),K=s*q(m),N=u*H(T),Q=u*q(T);if(w>y){var U=s*H(g),V=s*q(g),X=u*H(R),Y=u*q(R),E;if(F<an)if(E=pn(J,K,X,Y,U,V,N,Q)){var Z=J-E[0],$=K-E[1],b=U-E[0],k=V-E[1],nn=1/q(fn((Z*b+$*k)/(L(Z*Z+$*$)*L(b*b+k*k)))/2),en=L(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=W(X,Y,J,K,s,x,t),r=W(U,V,N,Q,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(J,K),a.arc(0,0,s,m,g,!t)):a.moveTo(J,K),!(u>y)||!(P>y)?a.lineTo(N,Q):p>y?(e=W(N,Q,U,V,u,-p,t),r=W(J,K,X,Y,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[H(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:B(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:B(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:B(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:B(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:B(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:B(+n),i):A},i.padAngle=function(n){return arguments.length?(C=typeof n=="function"?n:B(+n),i):C},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|