md-redline 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +169 -0
- package/bin/md-redline +255 -0
- package/bin/test-windows.ps1 +70 -0
- package/dist/assets/_baseFor-Ck08IaSF.js +1 -0
- package/dist/assets/arc-DI2g9LXK.js +1 -0
- package/dist/assets/architecture-YZFGNWBL-BDgMfc-b.js +1 -0
- package/dist/assets/architectureDiagram-Q4EWVU46-Dg1hcUEa.js +36 -0
- package/dist/assets/array-DOVTz2Mq.js +1 -0
- package/dist/assets/blockDiagram-DXYQGD6D-BAXkTCAk.js +132 -0
- package/dist/assets/c4Diagram-AHTNJAMY-BIkgwQSx.js +10 -0
- package/dist/assets/channel-DPCihw7y.js +1 -0
- package/dist/assets/chunk-2KRD3SAO-Dc_tBGsw.js +1 -0
- package/dist/assets/chunk-336JU56O-Dhi-ID9Y.js +2 -0
- package/dist/assets/chunk-426QAEUC-DnFdrNMW.js +1 -0
- package/dist/assets/chunk-4BX2VUAB-Z63FkGov.js +1 -0
- package/dist/assets/chunk-4TB4RGXK-BAiBlfyy.js +206 -0
- package/dist/assets/chunk-55IACEB6-BXDWXbxy.js +1 -0
- package/dist/assets/chunk-5FUZZQ4R-C72e1c_O.js +62 -0
- package/dist/assets/chunk-5PVQY5BW-BBHW_uCu.js +2 -0
- package/dist/assets/chunk-67CJDMHE-3Cf_D9m6.js +1 -0
- package/dist/assets/chunk-7N4EOEYR-DAXUXJ2c.js +1 -0
- package/dist/assets/chunk-AA7GKIK3-Dr7fOryc.js +1 -0
- package/dist/assets/chunk-BSJP7CBP-BmsSs1Nt.js +1 -0
- package/dist/assets/chunk-CIAEETIT-QDzV-X_Y.js +1 -0
- package/dist/assets/chunk-EDXVE4YY-C25WFHxY.js +1 -0
- package/dist/assets/chunk-ENJZ2VHE-_OzxcZOU.js +10 -0
- package/dist/assets/chunk-FMBD7UC4-CjsTKY4u.js +15 -0
- package/dist/assets/chunk-FOC6F5B3-g-xaH5nc.js +1 -0
- package/dist/assets/chunk-ICPOFSXX-iKiUSjDK.js +121 -0
- package/dist/assets/chunk-K5T4RW27-CKR-lPBN.js +94 -0
- package/dist/assets/chunk-KGLVRYIC-DRccT-B_.js +1 -0
- package/dist/assets/chunk-LIHQZDEY-DTbMwMXj.js +1 -0
- package/dist/assets/chunk-ORNJ4GCN-DlerdcWX.js +1 -0
- package/dist/assets/chunk-OYMX7WX6-Dekv1on2.js +231 -0
- package/dist/assets/chunk-QZHKN3VN-BHu0RdKl.js +1 -0
- package/dist/assets/chunk-U2HBQHQK-BvtlVHAg.js +70 -0
- package/dist/assets/chunk-X2U36JSP-BI_g8mub.js +1 -0
- package/dist/assets/chunk-XPW4576I-B39JkmSE.js +32 -0
- package/dist/assets/chunk-YZCP3GAM-BfPcXRm2.js +1 -0
- package/dist/assets/chunk-ZZ45TVLE-Bg4q68wZ.js +1 -0
- package/dist/assets/classDiagram-6PBFFD2Q-p73p727_.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-C4Ftpivp.js +1 -0
- package/dist/assets/clone-CI9aUwHe.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-7BpAeDh5.js +1 -0
- package/dist/assets/cytoscape.esm-DoTFyJaN.js +321 -0
- package/dist/assets/dagre-CilMRazv.js +1 -0
- package/dist/assets/dagre-KV5264BT-DDMqpjkB.js +4 -0
- package/dist/assets/defaultLocale-Ck2Xxk-C.js +1 -0
- package/dist/assets/diagram-5BDNPKRD-BFeyfnCx.js +10 -0
- package/dist/assets/diagram-G4DWMVQ6-DoqT-PtF.js +24 -0
- package/dist/assets/diagram-MMDJMWI5-BPV6KADk.js +43 -0
- package/dist/assets/diagram-TYMM5635-okvcTBtl.js +24 -0
- package/dist/assets/dist-C_eddq6m.js +1 -0
- package/dist/assets/erDiagram-SMLLAGMA-Dl-Ixy8n.js +85 -0
- package/dist/assets/flatten-B8XIuT0x.js +1 -0
- package/dist/assets/flowDiagram-DWJPFMVM-CsqWAx5r.js +162 -0
- package/dist/assets/ganttDiagram-T4ZO3ILL-mIt6zVeF.js +292 -0
- package/dist/assets/gitGraph-7Q5UKJZL-COXHGMvj.js +1 -0
- package/dist/assets/gitGraphDiagram-UUTBAWPF-syVqZJX_.js +106 -0
- package/dist/assets/graphlib-Bpd0q3yO.js +1 -0
- package/dist/assets/index-BoggyWS0.css +2 -0
- package/dist/assets/index-aLvjHQW4.js +104 -0
- package/dist/assets/info-OMHHGYJF-B-0wfxwL.js +1 -0
- package/dist/assets/infoDiagram-42DDH7IO-C0_uqsVa.js +2 -0
- package/dist/assets/init-Bft5Ffpj.js +1 -0
- package/dist/assets/isEmpty-BrFi5AqV.js +1 -0
- package/dist/assets/ishikawaDiagram-UXIWVN3A-CTjFbDBV.js +70 -0
- package/dist/assets/journeyDiagram-VCZTEJTY-BDBcej1q.js +139 -0
- package/dist/assets/kanban-definition-6JOO6SKY-Ylgzakw7.js +89 -0
- package/dist/assets/katex-Uj9wLT16.js +265 -0
- package/dist/assets/line-CRxEwpOv.js +1 -0
- package/dist/assets/linear-PDPfFByd.js +1 -0
- package/dist/assets/mermaid-parser.core-CY-XNOOy.js +4 -0
- package/dist/assets/mermaid.core-BPlTADIX.js +11 -0
- package/dist/assets/mindmap-definition-QFDTVHPH-TefzJnBM.js +96 -0
- package/dist/assets/ordinal-DIg8h6NI.js +1 -0
- package/dist/assets/packet-4T2RLAQJ-BW1T_A-C.js +1 -0
- package/dist/assets/path-DfRbCp9y.js +1 -0
- package/dist/assets/pie-ZZUOXDRM-DkKU-SFu.js +1 -0
- package/dist/assets/pieDiagram-DEJITSTG-BCXuaeEy.js +30 -0
- package/dist/assets/quadrantDiagram-34T5L4WZ-VSBAicWL.js +7 -0
- package/dist/assets/radar-PYXPWWZC-CYvTacKJ.js +1 -0
- package/dist/assets/reduce-CV2X8n1a.js +1 -0
- package/dist/assets/requirementDiagram-MS252O5E-4NeL9Z6J.js +84 -0
- package/dist/assets/rough.esm-Bbn_-PMU.js +1 -0
- package/dist/assets/sankeyDiagram-XADWPNL6-DMBSDnrH.js +10 -0
- package/dist/assets/sequenceDiagram-FGHM5R23-DVpzcZUi.js +157 -0
- package/dist/assets/src-PKe5NtkK.js +1 -0
- package/dist/assets/stateDiagram-FHFEXIEX-BkHTlCjL.js +1 -0
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-nMeWu9fP.js +1 -0
- package/dist/assets/timeline-definition-GMOUNBTQ-CyLt92nf.js +120 -0
- package/dist/assets/treeView-SZITEDCU-BUgcJ4eR.js +1 -0
- package/dist/assets/treemap-W4RFUUIX-BIWGQ4Pw.js +1 -0
- package/dist/assets/vennDiagram-DHZGUBPP-BCK0xB_m.js +34 -0
- package/dist/assets/wardley-RL74JXVD-DMZZRlby.js +1 -0
- package/dist/assets/wardleyDiagram-NUSXRM2D-BisBgfsF.js +20 -0
- package/dist/assets/xychartDiagram-5P7HB3ND-D_REDciv.js +7 -0
- package/dist/favicon.svg +15 -0
- package/dist/index.html +14 -0
- package/dist/screenshot.png +0 -0
- package/index.html +13 -0
- package/package.json +105 -0
- package/public/favicon.svg +15 -0
- package/public/screenshot.png +0 -0
- package/server/index.test.ts +814 -0
- package/server/index.ts +736 -0
- package/server/preferences.test.ts +126 -0
- package/server/preferences.ts +76 -0
- package/src/App.tsx +1620 -0
- package/src/components/ActionButton.tsx +41 -0
- package/src/components/CommandPalette.tsx +191 -0
- package/src/components/CommentCard.tsx +556 -0
- package/src/components/CommentForm.tsx +285 -0
- package/src/components/CommentSidebar.tsx +428 -0
- package/src/components/ConfirmDialog.tsx +64 -0
- package/src/components/ContextMenu.tsx +220 -0
- package/src/components/DragHandles.tsx +48 -0
- package/src/components/FileExplorer.tsx +251 -0
- package/src/components/FileOpener.tsx +304 -0
- package/src/components/IconButton.tsx +32 -0
- package/src/components/KeyboardShortcutsPanel.tsx +136 -0
- package/src/components/MarkdownViewer.tsx +682 -0
- package/src/components/RawView.tsx +798 -0
- package/src/components/SearchBar.tsx +129 -0
- package/src/components/Separator.tsx +7 -0
- package/src/components/SettingsPanel.tsx +813 -0
- package/src/components/SplitIconButton.tsx +133 -0
- package/src/components/TabBar.tsx +594 -0
- package/src/components/TableOfContents.tsx +70 -0
- package/src/components/ThemeSelector.tsx +159 -0
- package/src/components/Toast.tsx +99 -0
- package/src/components/Toolbar.tsx +161 -0
- package/src/components/iconButtonVariants.ts +19 -0
- package/src/components/rawView.test.ts +291 -0
- package/src/contexts/SettingsContext.tsx +120 -0
- package/src/hooks/useAuthor.test.ts +58 -0
- package/src/hooks/useAuthor.ts +69 -0
- package/src/hooks/useAutoResize.ts +20 -0
- package/src/hooks/useCommentCardTriggers.ts +20 -0
- package/src/hooks/useComments.test.ts +773 -0
- package/src/hooks/useComments.ts +332 -0
- package/src/hooks/useContextMenu.ts +48 -0
- package/src/hooks/useContextMenuItems.ts +392 -0
- package/src/hooks/useDiffSnapshot.test.ts +130 -0
- package/src/hooks/useDiffSnapshot.ts +67 -0
- package/src/hooks/useDragHandles.ts +417 -0
- package/src/hooks/useFileWatcher.ts +45 -0
- package/src/hooks/useHeadingTracking.ts +84 -0
- package/src/hooks/useMermaidRenderer.ts +75 -0
- package/src/hooks/useModalState.ts +22 -0
- package/src/hooks/usePageVisible.test.ts +69 -0
- package/src/hooks/usePageVisible.ts +19 -0
- package/src/hooks/usePaneLayout.test.ts +108 -0
- package/src/hooks/usePaneLayout.ts +102 -0
- package/src/hooks/useRecentFiles.test.ts +103 -0
- package/src/hooks/useRecentFiles.ts +99 -0
- package/src/hooks/useResizablePanel.test.ts +84 -0
- package/src/hooks/useResizablePanel.ts +118 -0
- package/src/hooks/useSearch.test.ts +72 -0
- package/src/hooks/useSearch.ts +53 -0
- package/src/hooks/useSelection.ts +48 -0
- package/src/hooks/useSessionPersistence.test.ts +59 -0
- package/src/hooks/useSessionPersistence.ts +43 -0
- package/src/hooks/useTabs.test.ts +127 -0
- package/src/hooks/useTabs.ts +561 -0
- package/src/hooks/useThemePersistence.ts +41 -0
- package/src/hooks/useToast.ts +27 -0
- package/src/index.css +1047 -0
- package/src/lib/agent-prompts.test.ts +34 -0
- package/src/lib/agent-prompts.ts +68 -0
- package/src/lib/comment-editor-state.ts +6 -0
- package/src/lib/comment-parser.test.ts +1959 -0
- package/src/lib/comment-parser.ts +1021 -0
- package/src/lib/diff.test.ts +164 -0
- package/src/lib/diff.ts +139 -0
- package/src/lib/heading-slugs.test.ts +85 -0
- package/src/lib/heading-slugs.ts +44 -0
- package/src/lib/http.test.ts +43 -0
- package/src/lib/http.ts +29 -0
- package/src/lib/mermaid-highlights.test.ts +517 -0
- package/src/lib/mermaid-highlights.ts +936 -0
- package/src/lib/mermaid-renderer.test.ts +114 -0
- package/src/lib/mermaid-renderer.ts +89 -0
- package/src/lib/path-utils.test.ts +17 -0
- package/src/lib/path-utils.ts +7 -0
- package/src/lib/platform.test.ts +58 -0
- package/src/lib/platform.ts +14 -0
- package/src/lib/preferences-client.test.ts +177 -0
- package/src/lib/preferences-client.ts +94 -0
- package/src/lib/selection-resolver.test.ts +118 -0
- package/src/lib/selection-resolver.ts +37 -0
- package/src/lib/settings.test.ts +152 -0
- package/src/lib/settings.ts +78 -0
- package/src/lib/shortcut-label.tsx +18 -0
- package/src/lib/themes.ts +21 -0
- package/src/lib/visible-text.test.ts +86 -0
- package/src/lib/visible-text.ts +77 -0
- package/src/main.tsx +22 -0
- package/src/markdown/pipeline.test.ts +82 -0
- package/src/markdown/pipeline.ts +33 -0
- package/src/types.test.ts +43 -0
- package/src/types.ts +46 -0
- package/tsconfig.app.json +28 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +26 -0
- package/vite.config.ts +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dennis Ju
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# <img src="public/favicon.svg" width="30" align="center" /> md-redline
|
|
2
|
+
|
|
3
|
+
Review rendered markdown with inline comments that live in the file.
|
|
4
|
+
|
|
5
|
+
`mdr` is a local review tool for markdown files in human + AI agent workflows. Highlight text in the rendered document, leave feedback, then hand the same file to an agent. Comments are stored inline in the `.md` file itself, so agents can read and address them directly. No sidecar files, no database, no external service. The markdown file stays the source of truth.
|
|
6
|
+
|
|
7
|
+
Markdown has become a common working format for specs, prompts, and design docs between humans and agents. `mdr` gives that workflow review tooling closer to code review: rendered context, inline comments, and a clean diff after changes are made. As Sean Grove argues in [specs are the new code](https://www.youtube.com/watch?v=8rABwKRsec4), specs are becoming the primary unit of work in agentic development. You write and review the spec, agents write the code.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
**See the full review workflow in 30 seconds:**
|
|
12
|
+
|
|
13
|
+
https://github.com/user-attachments/assets/855a9d02-b0fd-4dec-b0a5-742871e8c181
|
|
14
|
+
|
|
15
|
+
## Review workflow
|
|
16
|
+
|
|
17
|
+
### Default: comments as agent instructions
|
|
18
|
+
|
|
19
|
+
1. Open a markdown file in `mdr`.
|
|
20
|
+
2. Highlight text and leave inline comments.
|
|
21
|
+
3. Copy the hand-off prompt.
|
|
22
|
+
4. Paste the prompt into your AI agent.
|
|
23
|
+
5. The agent edits the file, addresses the feedback, and removes the comment markers it handled.
|
|
24
|
+
6. Review the result in diff view.
|
|
25
|
+
|
|
26
|
+
### Optional: resolve workflow
|
|
27
|
+
|
|
28
|
+
Enable resolve mode in Settings for human review with explicit `open` and `resolved` states.
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
Prerequisite: Node 20 or newer.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx md-redline /path/to/spec.md
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This starts the local app if needed and opens it in your browser.
|
|
39
|
+
|
|
40
|
+
Or install globally:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install -g md-redline
|
|
44
|
+
mdr /path/to/spec.md # Open a file
|
|
45
|
+
mdr /path/to/dir # Open a directory
|
|
46
|
+
mdr --stop # Stop the running server
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`md-redline` also works as an alias for `mdr`.
|
|
50
|
+
|
|
51
|
+
## How comments are stored
|
|
52
|
+
|
|
53
|
+
Comments are stored as invisible HTML markers directly in the markdown, immediately before the text they refer to, so both humans and agents can work from the same file.
|
|
54
|
+
|
|
55
|
+
```markdown
|
|
56
|
+
Some text <!-- @comment{
|
|
57
|
+
"id":"uuid",
|
|
58
|
+
"anchor":"highlighted text",
|
|
59
|
+
"text":"Rewrite this section to be clearer.",
|
|
60
|
+
"author":"User",
|
|
61
|
+
"timestamp":"2026-03-26T12:00:00.000Z",
|
|
62
|
+
"replies":[]
|
|
63
|
+
} -->highlighted text continues here.
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
This makes feedback:
|
|
67
|
+
|
|
68
|
+
- visible to AI agents via a plain file read
|
|
69
|
+
- portable with the markdown file
|
|
70
|
+
- invisible in normal renderers (GitHub, VS Code preview)
|
|
71
|
+
|
|
72
|
+
## Who this is for
|
|
73
|
+
|
|
74
|
+
- **People writing specs, prompts, or design docs locally** with file-based AI agents
|
|
75
|
+
- **Teams reviewing docs before they are committed** or sent out for wider review
|
|
76
|
+
- **Anyone in a human + agent editing loop** who wants structured inline feedback in plain files
|
|
77
|
+
|
|
78
|
+
### Non-goals
|
|
79
|
+
|
|
80
|
+
- Not a collaborative multi-user editing tool.
|
|
81
|
+
- Not a replacement for GitHub PR reviews (use those once the file is in git).
|
|
82
|
+
- Not designed for untrusted content. This is a local dev tool for your own files.
|
|
83
|
+
|
|
84
|
+
## Features
|
|
85
|
+
|
|
86
|
+
### Review and commenting
|
|
87
|
+
|
|
88
|
+
- Inline comments anchored to rendered text, including overlapping comments
|
|
89
|
+
- Threaded replies and optional `open` / `resolved` review states
|
|
90
|
+
- Adjustable anchors with drag handles
|
|
91
|
+
- Rendered, raw, and diff views
|
|
92
|
+
- Hand-off prompt copying for one or multiple files
|
|
93
|
+
|
|
94
|
+
### Navigation and editing
|
|
95
|
+
|
|
96
|
+
- Multi-tab editing with session persistence and tab context menus
|
|
97
|
+
- File explorer, recent files, and native OS file picker
|
|
98
|
+
- Find in document (`Cmd+F`) with match navigation
|
|
99
|
+
- Table of contents with scroll spy
|
|
100
|
+
- Command palette (`Cmd+K`), keyboard shortcuts, and settings panel (`Cmd+,`)
|
|
101
|
+
- Resizable panels and right-click context menus
|
|
102
|
+
|
|
103
|
+
### Rendering and integrations
|
|
104
|
+
|
|
105
|
+
- Real-time reload via SSE when files change externally
|
|
106
|
+
- Mermaid diagram rendering with commentable text
|
|
107
|
+
- Customizable comment templates
|
|
108
|
+
- 8 themes: Light, Dark, Sepia, Nord, Solarized, GitHub, Rosé Pine, Catppuccin
|
|
109
|
+
|
|
110
|
+
## Supported platforms
|
|
111
|
+
|
|
112
|
+
- **macOS**: supported
|
|
113
|
+
- **Linux**: supported; system file picker requires `zenity`
|
|
114
|
+
- **Windows**: supported; system file picker uses PowerShell
|
|
115
|
+
|
|
116
|
+
## Security model
|
|
117
|
+
|
|
118
|
+
`mdr` is a local dev tool. The server reads and writes markdown files inside the current working directory and any path explicitly opened at startup or through the system file picker. File saves use atomic write-then-rename and mtime-based conflict detection to prevent data loss from concurrent edits.
|
|
119
|
+
|
|
120
|
+
Only run it in environments you trust. Mermaid SVG output is sanitized via DOMPurify before rendering.
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
### From source
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
git clone https://github.com/dejuknow/md-redline.git
|
|
128
|
+
cd md-redline
|
|
129
|
+
npm install
|
|
130
|
+
npm run dev
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Open the local URL printed by Vite (usually `http://localhost:5188`).
|
|
134
|
+
|
|
135
|
+
### Scripts
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npm run dev # Start dev server
|
|
139
|
+
npm run lint # Lint
|
|
140
|
+
npm test # Production build + unit tests
|
|
141
|
+
npm run test:e2e # Playwright E2E tests
|
|
142
|
+
npm run build # Production build
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Agent eval
|
|
146
|
+
|
|
147
|
+
The eval harness tests whether AI agents correctly read, address, and remove inline comments.
|
|
148
|
+
|
|
149
|
+
- `npm run eval:dry` validates eval fixtures
|
|
150
|
+
- `npm run eval` runs the full eval harness
|
|
151
|
+
- See [eval/README.md](./eval/README.md) for details
|
|
152
|
+
|
|
153
|
+
## Architecture
|
|
154
|
+
|
|
155
|
+
```text
|
|
156
|
+
bin/md-redline CLI entry point (invoked as `mdr` or `md-redline`)
|
|
157
|
+
server/index.ts Hono server for file I/O, browsing, SSE, and local integrations
|
|
158
|
+
src/App.tsx Main application shell
|
|
159
|
+
src/components/ Viewer, sidebar, raw view, diff view, TOC, explorer, settings, etc.
|
|
160
|
+
src/hooks/ State, persistence, selection, file watching, drag handles, tabs
|
|
161
|
+
src/lib/comment-parser.ts Inline comment parsing and mutation helpers
|
|
162
|
+
src/markdown/pipeline.ts Markdown rendering pipeline
|
|
163
|
+
eval/ Eval harness for agent behavior against inline comments
|
|
164
|
+
e2e/ Playwright end-to-end coverage
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## License
|
|
168
|
+
|
|
169
|
+
[MIT](./LICENSE)
|
package/bin/md-redline
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync, spawn } from 'child_process';
|
|
4
|
+
import { readFile, stat, unlink } from 'fs/promises';
|
|
5
|
+
import { homedir, tmpdir } from 'os';
|
|
6
|
+
import { dirname, join, resolve } from 'path';
|
|
7
|
+
import process from 'process';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
|
|
13
|
+
const APP_DIR = resolve(__dirname, '..');
|
|
14
|
+
const DEFAULT_SERVER_PORT = 3001;
|
|
15
|
+
const DEFAULT_CLIENT_PORT = 5188;
|
|
16
|
+
const MAX_PORT_SCAN = 10;
|
|
17
|
+
const PORT_FILE = join(tmpdir(), 'md-redline.port');
|
|
18
|
+
const START_TIMEOUT_MS = 15_000;
|
|
19
|
+
const POLL_INTERVAL_MS = 500;
|
|
20
|
+
|
|
21
|
+
function printHelp() {
|
|
22
|
+
console.log('Usage: mdr [file.md | directory]');
|
|
23
|
+
console.log(' mdr --stop');
|
|
24
|
+
console.log('');
|
|
25
|
+
console.log('Opens a markdown file or folder in mdr for inline commenting.');
|
|
26
|
+
console.log('Starts the app automatically if it is not already running.');
|
|
27
|
+
console.log('Supported platforms: macOS, Linux, and Windows.');
|
|
28
|
+
console.log('');
|
|
29
|
+
console.log('Options:');
|
|
30
|
+
console.log(' --stop Stop the running mdr server');
|
|
31
|
+
console.log(' -h, --help Show this help message');
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log('Alias: md-redline');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function expandHomePath(inputPath) {
|
|
37
|
+
if (inputPath === '~') return homedir();
|
|
38
|
+
if (inputPath.startsWith('~/') || inputPath.startsWith('~\\')) {
|
|
39
|
+
return resolve(homedir(), inputPath.slice(2));
|
|
40
|
+
}
|
|
41
|
+
return inputPath;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function resolveTarget(arg) {
|
|
45
|
+
if (!arg) return { file: '', dir: '' };
|
|
46
|
+
|
|
47
|
+
const target = resolve(expandHomePath(arg));
|
|
48
|
+
let targetStat;
|
|
49
|
+
try {
|
|
50
|
+
targetStat = await stat(target);
|
|
51
|
+
} catch {
|
|
52
|
+
throw new Error(`not found: ${arg}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (targetStat.isDirectory()) return { file: '', dir: target };
|
|
56
|
+
if (targetStat.isFile()) return { file: target, dir: '' };
|
|
57
|
+
throw new Error(`not a file or directory: ${arg}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function checkServer(port) {
|
|
61
|
+
try {
|
|
62
|
+
const response = await fetch(`http://localhost:${port}/api/config`, {
|
|
63
|
+
signal: AbortSignal.timeout(1_000),
|
|
64
|
+
});
|
|
65
|
+
return response.ok;
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function findServerPort() {
|
|
72
|
+
// Try the port file first (fast path)
|
|
73
|
+
try {
|
|
74
|
+
const saved = (await readFile(PORT_FILE, 'utf8')).trim();
|
|
75
|
+
const port = Number(saved);
|
|
76
|
+
if (port && await checkServer(port)) return port;
|
|
77
|
+
} catch {}
|
|
78
|
+
|
|
79
|
+
// Scan the port range as fallback
|
|
80
|
+
for (let p = DEFAULT_SERVER_PORT; p < DEFAULT_SERVER_PORT + MAX_PORT_SCAN; p++) {
|
|
81
|
+
if (await checkServer(p)) return p;
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function findClientPort() {
|
|
87
|
+
for (let p = DEFAULT_CLIENT_PORT; p < DEFAULT_CLIENT_PORT + MAX_PORT_SCAN; p++) {
|
|
88
|
+
try {
|
|
89
|
+
const response = await fetch(`http://localhost:${p}/__mdr__`, {
|
|
90
|
+
signal: AbortSignal.timeout(1_000),
|
|
91
|
+
});
|
|
92
|
+
if (response.ok && (await response.text()) === 'mdr') return p;
|
|
93
|
+
} catch {}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function delay(ms) {
|
|
99
|
+
return new Promise((resolveDelay) => setTimeout(resolveDelay, ms));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function spawnDetached(command, args) {
|
|
103
|
+
return new Promise((resolveSpawn, rejectSpawn) => {
|
|
104
|
+
const isWin = process.platform === 'win32';
|
|
105
|
+
const child = isWin
|
|
106
|
+
? spawn([command, ...args].join(' '), {
|
|
107
|
+
cwd: APP_DIR,
|
|
108
|
+
stdio: 'ignore',
|
|
109
|
+
shell: true,
|
|
110
|
+
windowsHide: true,
|
|
111
|
+
})
|
|
112
|
+
: spawn(command, args, {
|
|
113
|
+
cwd: APP_DIR,
|
|
114
|
+
detached: true,
|
|
115
|
+
stdio: 'ignore',
|
|
116
|
+
windowsHide: true,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
child.once('error', rejectSpawn);
|
|
120
|
+
child.once('spawn', () => {
|
|
121
|
+
child.unref();
|
|
122
|
+
resolveSpawn(child);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function killPort(port) {
|
|
128
|
+
try {
|
|
129
|
+
if (process.platform === 'win32') {
|
|
130
|
+
const output = execSync(`netstat -ano | findstr :${port} | findstr LISTENING`, { encoding: 'utf8' });
|
|
131
|
+
const pids = new Set(output.trim().split('\n').map(line => line.trim().split(/\s+/).pop()));
|
|
132
|
+
for (const pid of pids) {
|
|
133
|
+
if (pid && pid !== '0') {
|
|
134
|
+
try { execSync(`taskkill /PID ${pid} /F`, { stdio: 'ignore' }); } catch { /* process may have already exited */ }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
execSync(`lsof -ti:${port} | xargs kill 2>/dev/null`, { stdio: 'ignore' });
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
} catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function stopServer() {
|
|
147
|
+
const serverPort = await findServerPort();
|
|
148
|
+
if (!serverPort) {
|
|
149
|
+
console.log('mdr is not running.');
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
console.log('Stopping mdr...');
|
|
154
|
+
killPort(serverPort);
|
|
155
|
+
|
|
156
|
+
// Also kill the Vite client if running (only kill ports that respond)
|
|
157
|
+
const clientPort = await findClientPort();
|
|
158
|
+
if (clientPort) {
|
|
159
|
+
killPort(clientPort);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Clean up port file
|
|
163
|
+
try { await unlink(PORT_FILE); } catch {}
|
|
164
|
+
|
|
165
|
+
console.log('mdr stopped.');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function ensureServerRunning() {
|
|
169
|
+
const existing = await findServerPort();
|
|
170
|
+
if (existing) return;
|
|
171
|
+
|
|
172
|
+
console.log('Starting mdr...');
|
|
173
|
+
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
174
|
+
let childExited = false;
|
|
175
|
+
const child = await spawnDetached(npmCommand, ['run', 'dev']);
|
|
176
|
+
child.once('exit', () => {
|
|
177
|
+
childExited = true;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const deadline = Date.now() + START_TIMEOUT_MS;
|
|
181
|
+
// Wait for the API server
|
|
182
|
+
while (Date.now() < deadline) {
|
|
183
|
+
if (await findServerPort()) break;
|
|
184
|
+
if (childExited) {
|
|
185
|
+
throw new Error("app failed to start. Run 'npm run dev' manually for details.");
|
|
186
|
+
}
|
|
187
|
+
await delay(POLL_INTERVAL_MS);
|
|
188
|
+
}
|
|
189
|
+
if (!(await findServerPort())) {
|
|
190
|
+
throw new Error('server did not start within 15 seconds.');
|
|
191
|
+
}
|
|
192
|
+
// Also wait for Vite client to be ready
|
|
193
|
+
while (Date.now() < deadline) {
|
|
194
|
+
if (await findClientPort()) {
|
|
195
|
+
console.log('mdr is running.');
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
await delay(POLL_INTERVAL_MS);
|
|
199
|
+
}
|
|
200
|
+
// Server is up but client may still be starting — proceed anyway
|
|
201
|
+
console.log('mdr server is running (client may still be starting).');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function buildUrl(file, dir) {
|
|
205
|
+
const clientPort = await findClientPort() ?? DEFAULT_CLIENT_PORT;
|
|
206
|
+
const baseUrl = `http://localhost:${clientPort}`;
|
|
207
|
+
if (file) return `${baseUrl}?file=${encodeURIComponent(file)}`;
|
|
208
|
+
if (dir) return `${baseUrl}?dir=${encodeURIComponent(dir)}`;
|
|
209
|
+
return baseUrl;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async function openInBrowser(url) {
|
|
213
|
+
if (process.platform === 'darwin') {
|
|
214
|
+
await spawnDetached('open', [url]);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (process.platform === 'win32') {
|
|
218
|
+
await spawnDetached('cmd', ['/c', 'start', '', url]);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (process.platform === 'linux') {
|
|
222
|
+
await spawnDetached('xdg-open', [url]);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
console.log(`Open in your browser: ${url}`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function main() {
|
|
229
|
+
if (process.argv[2] === '-h' || process.argv[2] === '--help') {
|
|
230
|
+
printHelp();
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (process.argv[2] === '--stop') {
|
|
235
|
+
await stopServer();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const { file, dir } = await resolveTarget(process.argv[2] ?? '');
|
|
241
|
+
await ensureServerRunning();
|
|
242
|
+
const url = await buildUrl(file, dir);
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
await openInBrowser(url);
|
|
246
|
+
} catch {
|
|
247
|
+
console.log(`Open in your browser: ${url}`);
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error(`Error: ${error instanceof Error ? error.message : 'unknown error'}`);
|
|
251
|
+
process.exitCode = 1;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
await main();
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[switch]$Headed,
|
|
3
|
+
[switch]$UI,
|
|
4
|
+
[switch]$InstallOnly,
|
|
5
|
+
[string]$ProjectRoot = $PSScriptRoot + "\.."
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
# Move to project root
|
|
9
|
+
Push-Location $ProjectRoot
|
|
10
|
+
|
|
11
|
+
Write-Host "`n--- md-redline Windows Test Runner ---" -ForegroundColor Cyan
|
|
12
|
+
|
|
13
|
+
# 1. Check for Node.js
|
|
14
|
+
if (!(Get-Command node -ErrorAction SilentlyContinue)) {
|
|
15
|
+
Write-Host "Error: Node.js is not installed. Please install it from https://nodejs.org/" -ForegroundColor Red
|
|
16
|
+
Pop-Location
|
|
17
|
+
exit 1
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# 2. Install dependencies if node_modules is missing
|
|
21
|
+
if (!(Test-Path "node_modules")) {
|
|
22
|
+
Write-Host "node_modules not found. Installing dependencies..." -ForegroundColor Yellow
|
|
23
|
+
npm install
|
|
24
|
+
if ($LASTEXITCODE -ne 0) {
|
|
25
|
+
Write-Host "npm install failed." -ForegroundColor Red
|
|
26
|
+
Pop-Location
|
|
27
|
+
exit $LASTEXITCODE
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# 3. Ensure Playwright browsers are installed
|
|
32
|
+
Write-Host "Checking Playwright browsers..." -ForegroundColor Yellow
|
|
33
|
+
npx playwright install chromium --with-deps
|
|
34
|
+
if ($LASTEXITCODE -ne 0) {
|
|
35
|
+
Write-Host "Playwright installation failed." -ForegroundColor Red
|
|
36
|
+
Pop-Location
|
|
37
|
+
exit $LASTEXITCODE
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if ($InstallOnly) {
|
|
41
|
+
Write-Host "Setup complete! Browsers and dependencies are ready." -ForegroundColor Green
|
|
42
|
+
Pop-Location
|
|
43
|
+
exit 0
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# 4. Determine command arguments
|
|
47
|
+
$playwrightArgs = @("test")
|
|
48
|
+
if ($Headed) {
|
|
49
|
+
$playwrightArgs += "--headed"
|
|
50
|
+
Write-Host "Running in HEADED mode..." -ForegroundColor Cyan
|
|
51
|
+
}
|
|
52
|
+
if ($UI) {
|
|
53
|
+
$playwrightArgs += "--ui"
|
|
54
|
+
Write-Host "Opening Playwright UI..." -ForegroundColor Cyan
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# 5. Run the tests
|
|
58
|
+
Write-Host "Starting tests...`n" -ForegroundColor Green
|
|
59
|
+
npx playwright $playwrightArgs
|
|
60
|
+
|
|
61
|
+
$exitCode = $LASTEXITCODE
|
|
62
|
+
Pop-Location
|
|
63
|
+
|
|
64
|
+
if ($exitCode -eq 0) {
|
|
65
|
+
Write-Host "`nTests passed successfully!" -ForegroundColor Green
|
|
66
|
+
} else {
|
|
67
|
+
Write-Host "`nTests failed with exit code $exitCode" -ForegroundColor Red
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
exit $exitCode
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(){this.__data__=[],this.size=0}function t(e,t){return e===t||e!==e&&t!==t}function n(e,n){for(var r=e.length;r--;)if(t(e[r][0],n))return r;return-1}var r=Array.prototype.splice;function i(e){var t=this.__data__,i=n(t,e);return i<0?!1:(i==t.length-1?t.pop():r.call(t,i,1),--this.size,!0)}function a(e){var t=this.__data__,r=n(t,e);return r<0?void 0:t[r][1]}function o(e){return n(this.__data__,e)>-1}function s(e,t){var r=this.__data__,i=n(r,e);return i<0?(++this.size,r.push([e,t])):r[i][1]=t,this}function c(e){var t=-1,n=e==null?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}c.prototype.clear=e,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s;function l(){this.__data__=new c,this.size=0}function ee(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n}function te(e){return this.__data__.get(e)}function ne(e){return this.__data__.has(e)}var u=typeof global==`object`&&global&&global.Object===Object&&global,re=typeof self==`object`&&self&&self.Object===Object&&self,d=u||re||Function(`return this`)(),f=d.Symbol,p=Object.prototype,ie=p.hasOwnProperty,ae=p.toString,m=f?f.toStringTag:void 0;function oe(e){var t=ie.call(e,m),n=e[m];try{e[m]=void 0;var r=!0}catch{}var i=ae.call(e);return r&&(t?e[m]=n:delete e[m]),i}var se=Object.prototype.toString;function ce(e){return se.call(e)}var le=`[object Null]`,ue=`[object Undefined]`,h=f?f.toStringTag:void 0;function g(e){return e==null?e===void 0?ue:le:h&&h in Object(e)?oe(e):ce(e)}function _(e){var t=typeof e;return e!=null&&(t==`object`||t==`function`)}var de=`[object AsyncFunction]`,fe=`[object Function]`,pe=`[object GeneratorFunction]`,me=`[object Proxy]`;function v(e){if(!_(e))return!1;var t=g(e);return t==fe||t==pe||t==de||t==me}var y=d[`__core-js_shared__`],b=function(){var e=/[^.]+$/.exec(y&&y.keys&&y.keys.IE_PROTO||``);return e?`Symbol(src)_1.`+e:``}();function he(e){return!!b&&b in e}var ge=Function.prototype.toString;function x(e){if(e!=null){try{return ge.call(e)}catch{}try{return e+``}catch{}}return``}var _e=/[\\^$.*+?()[\]{}|]/g,ve=/^\[object .+?Constructor\]$/,ye=Function.prototype,be=Object.prototype,xe=ye.toString,Se=be.hasOwnProperty,Ce=RegExp(`^`+xe.call(Se).replace(_e,`\\$&`).replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,`$1.*?`)+`$`);function we(e){return!_(e)||he(e)?!1:(v(e)?Ce:ve).test(x(e))}function Te(e,t){return e?.[t]}function S(e,t){var n=Te(e,t);return we(n)?n:void 0}var C=S(d,`Map`),w=S(Object,`create`);function T(){this.__data__=w?w(null):{},this.size=0}function E(e){var t=this.has(e)&&delete this.__data__[e];return this.size-=t?1:0,t}var D=`__lodash_hash_undefined__`,O=Object.prototype.hasOwnProperty;function Ee(e){var t=this.__data__;if(w){var n=t[e];return n===D?void 0:n}return O.call(t,e)?t[e]:void 0}var De=Object.prototype.hasOwnProperty;function Oe(e){var t=this.__data__;return w?t[e]!==void 0:De.call(t,e)}var ke=`__lodash_hash_undefined__`;function Ae(e,t){var n=this.__data__;return this.size+=this.has(e)?0:1,n[e]=w&&t===void 0?ke:t,this}function k(e){var t=-1,n=e==null?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}k.prototype.clear=T,k.prototype.delete=E,k.prototype.get=Ee,k.prototype.has=Oe,k.prototype.set=Ae;function je(){this.size=0,this.__data__={hash:new k,map:new(C||c),string:new k}}function Me(e){var t=typeof e;return t==`string`||t==`number`||t==`symbol`||t==`boolean`?e!==`__proto__`:e===null}function A(e,t){var n=e.__data__;return Me(t)?n[typeof t==`string`?`string`:`hash`]:n.map}function Ne(e){var t=A(this,e).delete(e);return this.size-=t?1:0,t}function Pe(e){return A(this,e).get(e)}function Fe(e){return A(this,e).has(e)}function Ie(e,t){var n=A(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this}function j(e){var t=-1,n=e==null?0:e.length;for(this.clear();++t<n;){var r=e[t];this.set(r[0],r[1])}}j.prototype.clear=je,j.prototype.delete=Ne,j.prototype.get=Pe,j.prototype.has=Fe,j.prototype.set=Ie;var Le=200;function Re(e,t){var n=this.__data__;if(n instanceof c){var r=n.__data__;if(!C||r.length<Le-1)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new j(r)}return n.set(e,t),this.size=n.size,this}function M(e){this.size=(this.__data__=new c(e)).size}M.prototype.clear=l,M.prototype.delete=ee,M.prototype.get=te,M.prototype.has=ne,M.prototype.set=Re;var ze=d.Uint8Array,N=Array.isArray;function Be(e,t){for(var n=-1,r=Array(e);++n<e;)r[n]=t(n);return r}function P(e){return typeof e==`object`&&!!e}var Ve=`[object Arguments]`;function F(e){return P(e)&&g(e)==Ve}var I=Object.prototype,He=I.hasOwnProperty,Ue=I.propertyIsEnumerable,L=F(function(){return arguments}())?F:function(e){return P(e)&&He.call(e,`callee`)&&!Ue.call(e,`callee`)};function We(){return!1}var R=typeof exports==`object`&&exports&&!exports.nodeType&&exports,z=R&&typeof module==`object`&&module&&!module.nodeType&&module,B=z&&z.exports===R?d.Buffer:void 0,V=(B?B.isBuffer:void 0)||We,Ge=9007199254740991,Ke=/^(?:0|[1-9]\d*)$/;function H(e,t){var n=typeof e;return t??=Ge,!!t&&(n==`number`||n!=`symbol`&&Ke.test(e))&&e>-1&&e%1==0&&e<t}var qe=9007199254740991;function U(e){return typeof e==`number`&&e>-1&&e%1==0&&e<=qe}var Je=`[object Arguments]`,Ye=`[object Array]`,Xe=`[object Boolean]`,Ze=`[object Date]`,Qe=`[object Error]`,$e=`[object Function]`,et=`[object Map]`,tt=`[object Number]`,nt=`[object Object]`,rt=`[object RegExp]`,it=`[object Set]`,at=`[object String]`,ot=`[object WeakMap]`,st=`[object ArrayBuffer]`,ct=`[object DataView]`,lt=`[object Float32Array]`,ut=`[object Float64Array]`,dt=`[object Int8Array]`,ft=`[object Int16Array]`,pt=`[object Int32Array]`,mt=`[object Uint8Array]`,ht=`[object Uint8ClampedArray]`,gt=`[object Uint16Array]`,_t=`[object Uint32Array]`,W={};W[lt]=W[ut]=W[dt]=W[ft]=W[pt]=W[mt]=W[ht]=W[gt]=W[_t]=!0,W[Je]=W[Ye]=W[st]=W[Xe]=W[ct]=W[Ze]=W[Qe]=W[$e]=W[et]=W[tt]=W[nt]=W[rt]=W[it]=W[at]=W[ot]=!1;function vt(e){return P(e)&&U(e.length)&&!!W[g(e)]}function G(e){return function(t){return e(t)}}var K=typeof exports==`object`&&exports&&!exports.nodeType&&exports,q=K&&typeof module==`object`&&module&&!module.nodeType&&module,J=q&&q.exports===K&&u.process,Y=function(){try{return q&&q.require&&q.require(`util`).types||J&&J.binding&&J.binding(`util`)}catch{}}(),X=Y&&Y.isTypedArray,Z=X?G(X):vt,yt=Object.prototype.hasOwnProperty;function bt(e,t){var n=N(e),r=!n&&L(e),i=!n&&!r&&V(e),a=!n&&!r&&!i&&Z(e),o=n||r||i||a,s=o?Be(e.length,String):[],c=s.length;for(var l in e)(t||yt.call(e,l))&&!(o&&(l==`length`||i&&(l==`offset`||l==`parent`)||a&&(l==`buffer`||l==`byteLength`||l==`byteOffset`)||H(l,c)))&&s.push(l);return s}var xt=Object.prototype;function St(e){var t=e&&e.constructor;return e===(typeof t==`function`&&t.prototype||xt)}function Ct(e,t){return function(n){return e(t(n))}}function wt(e){return e!=null&&U(e.length)&&!v(e)}var Tt=`Expected a function`;function Q(e,t){if(typeof e!=`function`||t!=null&&typeof t!=`function`)throw TypeError(Tt);var n=function(){var r=arguments,i=t?t.apply(this,r):r[0],a=n.cache;if(a.has(i))return a.get(i);var o=e.apply(this,r);return n.cache=a.set(i,o)||a,o};return n.cache=new(Q.Cache||j),n}Q.Cache=j;function $(e){return e}function Et(e){return function(t,n,r){for(var i=-1,a=Object(t),o=r(t),s=o.length;s--;){var c=o[e?s:++i];if(n(a[c],c,a)===!1)break}return t}}var Dt=Et();export{v as C,d as D,f as E,t as O,x as S,g as T,ze as _,Ct as a,C as b,Z as c,U as d,H as f,N as g,P as h,wt as i,Y as l,L as m,$ as n,St as o,V as p,Q as r,bt as s,Dt as t,G as u,M as v,_ as w,S as x,j as y};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e,t}from"./path-DfRbCp9y.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-C_eddq6m.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i)if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./chunk-K5T4RW27-CKR-lPBN.js";import{n as e}from"./chunk-7N4EOEYR-DAXUXJ2c.js";export{e as createArchitectureServices};
|