dsh-oc-tui 0.2.0-pre.13 → 0.2.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 +13 -6
- package/docs//347/224/250/346/210/267/346/211/213/345/206/214.md +39 -6
- package/dsh-plugin.json +5 -1
- package/lib/diagnostics.js +120 -0
- package/lib/git-branch.js +157 -0
- package/lib/image.js +5 -1
- package/lib/index.js +904 -19
- package/lib/locales/ar.js +637 -560
- package/lib/locales/de.js +606 -529
- package/lib/locales/en.js +79 -0
- package/lib/locales/es.js +606 -529
- package/lib/locales/fr.js +606 -529
- package/lib/locales/id.js +608 -531
- package/lib/locales/it.js +606 -529
- package/lib/locales/ja.js +606 -529
- package/lib/locales/ko.js +606 -529
- package/lib/locales/nl.js +606 -529
- package/lib/locales/pl.js +620 -543
- package/lib/locales/pt-BR.js +606 -529
- package/lib/locales/ru.js +620 -543
- package/lib/locales/tr.js +606 -529
- package/lib/locales/vi.js +608 -531
- package/lib/locales/zh-CN.js +607 -530
- package/lib/locales/zh-TW.js +607 -530
- package/lib/mermaid-ascii.js +20 -1
- package/lib/mermaid.js +39 -20
- package/lib/providers.js +712 -0
- package/lib/splash-opencode.js +27 -9
- package/lib/std/command-list.js +2 -1
- package/lib/term.js +13 -1
- package/lib/ui.js +326 -20
- package/lib/util.js +49 -0
- package/lib/web-settings.js +182 -142
- package/package.json +5 -4
package/lib/mermaid-ascii.js
CHANGED
|
@@ -43,6 +43,14 @@ export function loadMermaidEngine() {
|
|
|
43
43
|
return enginePromise
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// One entry per (engine, source). The layout depends on the source alone, but
|
|
47
|
+
// the transcript's per-block line cache is keyed by revision and width — so a
|
|
48
|
+
// resize, or any edit above the fence, re-paints every diagram in the
|
|
49
|
+
// transcript. Without this cache each of those repaints re-runs the layout for
|
|
50
|
+
// every fence on screen.
|
|
51
|
+
const ART_CACHE = new WeakMap()
|
|
52
|
+
const ART_CACHE_MAX = 64
|
|
53
|
+
|
|
46
54
|
/**
|
|
47
55
|
* Lay one mermaid source out as box-drawing art.
|
|
48
56
|
* @param {{render: Function}|null} engine - from {@link loadMermaidEngine}.
|
|
@@ -52,7 +60,18 @@ export function loadMermaidEngine() {
|
|
|
52
60
|
*/
|
|
53
61
|
export function renderMermaidArt(engine, src) {
|
|
54
62
|
if (!engine || typeof src !== 'string' || src.trim() === '') return null
|
|
55
|
-
|
|
63
|
+
let bySource = ART_CACHE.get(engine)
|
|
64
|
+
if (!bySource) {
|
|
65
|
+
bySource = new Map()
|
|
66
|
+
ART_CACHE.set(engine, bySource)
|
|
67
|
+
}
|
|
68
|
+
if (bySource.has(src)) return bySource.get(src)
|
|
69
|
+
// A null answer is cached too: a grammar the engine cannot draw would
|
|
70
|
+
// otherwise be re-parsed on every repaint, only to fail the same way.
|
|
71
|
+
const art = engine.render(emojiWideSource(engine, src))
|
|
72
|
+
if (bySource.size >= ART_CACHE_MAX) bySource.delete(bySource.keys().next().value)
|
|
73
|
+
bySource.set(src, art)
|
|
74
|
+
return art
|
|
56
75
|
}
|
|
57
76
|
|
|
58
77
|
// The engine sizes a label from the `unicode-width` crate, which follows
|
package/lib/mermaid.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// 'local' never touches the network, 'off' renders nothing.
|
|
13
13
|
import { spawn } from 'node:child_process'
|
|
14
14
|
import { createHash } from 'node:crypto'
|
|
15
|
-
import { readFile, mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
15
|
+
import { mkdir, readFile, mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
16
16
|
import { tmpdir } from 'node:os'
|
|
17
17
|
import { join } from 'node:path'
|
|
18
18
|
|
|
@@ -24,8 +24,14 @@ const sha1 = (s) => createHash('sha1').update(s).digest('hex')
|
|
|
24
24
|
|
|
25
25
|
// Deterministic cache file name for a diagram (encoded diagrams are long and
|
|
26
26
|
// URL-unsafe; a content hash keeps the cache dir tidy and stable).
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
//
|
|
28
|
+
// The requested pixel width is part of the key: the same diagram rendered for a
|
|
29
|
+
// narrow terminal is a different raster from the one a wide terminal asks for,
|
|
30
|
+
// and serving the narrow file to the wide terminal would draw a diagram at half
|
|
31
|
+
// the width of the space reserved for it.
|
|
32
|
+
export function mermaidCachePath(code, pxWidth) {
|
|
33
|
+
const width = pxWidth ? ':' + Math.round(pxWidth) : ''
|
|
34
|
+
return sha1('mermaid:' + code + width).slice(0, 24) + '.png'
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
function spawnWithTimeout(cmd, args, timeoutMs) {
|
|
@@ -89,10 +95,13 @@ export function createMermaidRenderer({ fetch: fetchImpl = globalThis.fetch, mod
|
|
|
89
95
|
}
|
|
90
96
|
}
|
|
91
97
|
|
|
92
|
-
async function renderViaInk(code) {
|
|
98
|
+
async function renderViaInk(code, { pxWidth }) {
|
|
93
99
|
if (typeof fetchImpl !== 'function') return null
|
|
94
100
|
const encoded = Buffer.from(code, 'utf8').toString('base64url')
|
|
95
|
-
|
|
101
|
+
// `width` is the ink service's own pixel hint; without it the diagram comes
|
|
102
|
+
// back at the service's default and is downscaled here for no reason.
|
|
103
|
+
const width = Math.min(2000, Math.max(64, Math.round(pxWidth ?? 800)))
|
|
104
|
+
const url = 'https://mermaid.ink/img/' + encoded + '?type=png&width=' + width
|
|
96
105
|
const res = await fetchImpl(url, { signal: AbortSignal.timeout(INK_TIMEOUT_MS) })
|
|
97
106
|
if (!res.ok) return null
|
|
98
107
|
const buf = Buffer.from(await res.arrayBuffer())
|
|
@@ -105,31 +114,41 @@ export function createMermaidRenderer({ fetch: fetchImpl = globalThis.fetch, mod
|
|
|
105
114
|
if (effectiveMode === 'off') return null
|
|
106
115
|
code = String(code ?? '')
|
|
107
116
|
if (code.trim() === '') return null
|
|
108
|
-
|
|
117
|
+
// Width is part of the memory key for the same reason it is part of the
|
|
118
|
+
// file name: a resize asks for a different raster.
|
|
119
|
+
const memoKey = code + '\u0000' + (pxWidth ?? 0)
|
|
120
|
+
const cached = memory.get(memoKey)
|
|
109
121
|
if (cached) return cached
|
|
110
122
|
|
|
111
123
|
let result = null
|
|
112
|
-
|
|
124
|
+
const cacheFile = cacheDir ? join(cacheDir, mermaidCachePath(code, pxWidth)) : null
|
|
125
|
+
// Disk before any provider. A diagram this machine already rendered is a
|
|
126
|
+
// file read; going to the network first made every session pay for a fresh
|
|
127
|
+
// render of diagrams that were sitting on disk, and sent the diagram text
|
|
128
|
+
// to a public service for nothing.
|
|
129
|
+
if (cacheFile) {
|
|
130
|
+
try {
|
|
131
|
+
const bytes = await readFile(cacheFile)
|
|
132
|
+
if (bytes.length > 0) result = { bytes, mediaType: 'image/png' }
|
|
133
|
+
} catch { /* no cached file */ }
|
|
134
|
+
}
|
|
135
|
+
if (!result && (effectiveMode === 'auto' || effectiveMode === 'local')) {
|
|
113
136
|
result = await renderViaMmdc(code, { pxWidth })
|
|
114
137
|
}
|
|
115
138
|
if (!result && (effectiveMode === 'auto' || effectiveMode === 'network')) {
|
|
116
139
|
try {
|
|
117
|
-
result = await renderViaInk(code)
|
|
140
|
+
result = await renderViaInk(code, { pxWidth })
|
|
118
141
|
} catch { /* offline or blocked; fall through */ }
|
|
119
142
|
}
|
|
120
|
-
if (!result && cacheDir) {
|
|
121
|
-
// A previous session may have rendered this exact diagram to disk.
|
|
122
|
-
try {
|
|
123
|
-
const path = join(cacheDir, mermaidCachePath(code))
|
|
124
|
-
const bytes = await readFile(path)
|
|
125
|
-
if (bytes.length > 0) result = { bytes, mediaType: 'image/png' }
|
|
126
|
-
} catch { /* no cached file */ }
|
|
127
|
-
}
|
|
128
143
|
if (result) {
|
|
129
|
-
memory.set(
|
|
130
|
-
if (
|
|
131
|
-
|
|
132
|
-
|
|
144
|
+
memory.set(memoKey, result)
|
|
145
|
+
if (cacheFile) {
|
|
146
|
+
// The cache directory is created on first write: it does not exist on a
|
|
147
|
+
// fresh install, and a write into a missing directory fails silently —
|
|
148
|
+
// which is how the disk cache went unused on every run.
|
|
149
|
+
void mkdir(cacheDir, { recursive: true })
|
|
150
|
+
.then(() => writeFile(cacheFile, result.bytes))
|
|
151
|
+
.catch(() => { /* best-effort disk cache */ })
|
|
133
152
|
}
|
|
134
153
|
}
|
|
135
154
|
return result
|