@code-yeongyu/senpi-tui 2026.7.25-2 → 2026.7.28
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/dist/components/editor.d.ts +22 -6
- package/dist/components/editor.d.ts.map +1 -1
- package/dist/components/editor.js +56 -120
- package/dist/components/editor.js.map +1 -1
- package/dist/components/image.d.ts.map +1 -1
- package/dist/components/image.js +7 -1
- package/dist/components/image.js.map +1 -1
- package/dist/components/select-list.d.ts +32 -0
- package/dist/components/select-list.d.ts.map +1 -1
- package/dist/components/select-list.js +15 -7
- package/dist/components/select-list.js.map +1 -1
- package/dist/editor-component.d.ts +18 -0
- package/dist/editor-component.d.ts.map +1 -1
- package/dist/editor-component.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/paste-markers.d.ts +29 -0
- package/dist/paste-markers.d.ts.map +1 -0
- package/dist/paste-markers.js +185 -0
- package/dist/paste-markers.js.map +1 -0
- package/dist/terminal-capabilities.d.ts +18 -0
- package/dist/terminal-capabilities.d.ts.map +1 -0
- package/dist/terminal-capabilities.js +62 -0
- package/dist/terminal-capabilities.js.map +1 -0
- package/dist/terminal-image.d.ts +31 -6
- package/dist/terminal-image.d.ts.map +1 -1
- package/dist/terminal-image.js +137 -92
- package/dist/terminal-image.js.map +1 -1
- package/dist/tmux-focus.d.ts +8 -0
- package/dist/tmux-focus.d.ts.map +1 -0
- package/dist/tmux-focus.js +14 -0
- package/dist/tmux-focus.js.map +1 -0
- package/dist/tmux-image-capability.d.ts +17 -0
- package/dist/tmux-image-capability.d.ts.map +1 -0
- package/dist/tmux-image-capability.js +61 -0
- package/dist/tmux-image-capability.js.map +1 -0
- package/dist/tmux-image-probe.d.ts +39 -0
- package/dist/tmux-image-probe.d.ts.map +1 -0
- package/dist/tmux-image-probe.js +121 -0
- package/dist/tmux-image-probe.js.map +1 -0
- package/dist/tui.d.ts +3 -0
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +63 -33
- package/dist/tui.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +19 -0
- package/dist/utils.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paste-markers.js","sourceRoot":"","sources":["../src/paste-markers.ts"],"names":[],"mappings":"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","sourcesContent":["const PASTE_MARKER_REGEX = /\\[paste #(\\d+)( (\\+\\d+ lines|\\d+ chars))?\\]/g;\nconst PASTE_MARKER_SINGLE = /^\\[paste #(\\d+)( (\\+\\d+ lines|\\d+ chars))?\\]$/;\n\ninterface PasteMarkerEntry {\n\tcontent: string;\n\tmarker: string;\n\tlineCount: number;\n\tcharCount: number;\n}\n\nexport interface EditorPasteState {\n\tpastes: ReadonlyMap<number, string>;\n\tmarkers?: ReadonlyMap<number, string>;\n\tpasteCounter: number;\n}\n\nexport interface PasteMarkerRemoval {\n\ttext: string;\n\tremoved: boolean;\n}\n\nfunction formatPasteMarker(id: number, lineCount: number, charCount: number): string {\n\treturn lineCount > 10 ? `[paste #${id} +${lineCount} lines]` : `[paste #${id} ${charCount} chars]`;\n}\n\nfunction countLines(content: string): number {\n\tlet lineCount = 1;\n\tfor (let index = 0; index < content.length; index++) {\n\t\tif (content.charCodeAt(index) === 10) lineCount++;\n\t}\n\treturn lineCount;\n}\n\nfunction createEntry(id: number, content: string, marker?: string): PasteMarkerEntry {\n\tconst charCount = content.length;\n\tconst parsed = marker?.match(PASTE_MARKER_SINGLE);\n\tconst lineMatch = parsed?.[3]?.match(/^\\+(\\d+) lines$/);\n\tconst lineCount = lineMatch ? Number.parseInt(lineMatch[1]!, 10) : countLines(content);\n\tconst canonical = formatPasteMarker(id, lineCount, charCount);\n\treturn { content, marker: marker === canonical ? marker : canonical, lineCount, charCount };\n}\n\nfunction countOccurrences(text: string, marker: string): number {\n\tlet count = 0;\n\tlet offset = 0;\n\twhile (offset <= text.length - marker.length) {\n\t\tconst index = text.indexOf(marker, offset);\n\t\tif (index === -1) break;\n\t\tcount++;\n\t\toffset = index + marker.length;\n\t}\n\treturn count;\n}\n\nfunction replaceSingleOccurrence(text: string, marker: string, replacement: string): string {\n\tconst index = text.indexOf(marker);\n\tif (index === -1 || text.indexOf(marker, index + marker.length) !== -1) return text;\n\treturn text.slice(0, index) + replacement + text.slice(index + marker.length);\n}\n\nfunction entriesFromState(state: EditorPasteState): Map<number, PasteMarkerEntry> {\n\tconst entries = new Map<number, PasteMarkerEntry>();\n\tfor (const [id, content] of state.pastes) {\n\t\tentries.set(id, createEntry(id, content, state.markers?.get(id)));\n\t}\n\treturn entries;\n}\n\nexport function expandPasteMarkers(text: string, state: EditorPasteState): string {\n\tconst entries = entriesFromState(state);\n\tconst byMarker = new Map<string, PasteMarkerEntry>();\n\tfor (const entry of entries.values()) byMarker.set(entry.marker, entry);\n\n\tconst matches: Array<{ end: number; marker: string; start: number }> = [];\n\tconst counts = new Map<string, number>();\n\tfor (const match of text.matchAll(PASTE_MARKER_REGEX)) {\n\t\tconst marker = match[0];\n\t\tif (!byMarker.has(marker)) continue;\n\t\tmatches.push({ start: match.index, end: match.index + marker.length, marker });\n\t\tcounts.set(marker, (counts.get(marker) ?? 0) + 1);\n\t}\n\tif (matches.length === 0) return text;\n\n\tconst chunks: string[] = [];\n\tlet offset = 0;\n\tfor (const match of matches) {\n\t\tchunks.push(text.slice(offset, match.start));\n\t\tconst entry = byMarker.get(match.marker)!;\n\t\tchunks.push(counts.get(match.marker) === 1 ? entry.content : match.marker);\n\t\toffset = match.end;\n\t}\n\tchunks.push(text.slice(offset));\n\treturn chunks.join(\"\");\n}\n\nexport function isPasteMarker(segment: string): boolean {\n\treturn segment.length >= 10 && PASTE_MARKER_SINGLE.test(segment);\n}\n\nexport function pasteMarkerId(segment: string): number | undefined {\n\tconst match = segment.length >= 10 ? PASTE_MARKER_SINGLE.exec(segment) : null;\n\treturn match ? Number.parseInt(match[1]!, 10) : undefined;\n}\n\nexport function segmentWithPasteMarkers(\n\ttext: string,\n\tbaseSegmenter: Intl.Segmenter,\n\tvalidMarkers: ReadonlySet<string>,\n): Iterable<Intl.SegmentData> {\n\tif (validMarkers.size === 0 || !text.includes(\"[paste #\")) return baseSegmenter.segment(text);\n\n\tconst markers: Array<{ start: number; end: number }> = [];\n\tfor (const match of text.matchAll(PASTE_MARKER_REGEX)) {\n\t\tif (validMarkers.has(match[0])) markers.push({ start: match.index, end: match.index + match[0].length });\n\t}\n\tif (markers.length === 0) return baseSegmenter.segment(text);\n\n\tconst result: Intl.SegmentData[] = [];\n\tlet markerIndex = 0;\n\tlet marker = markers[0];\n\tfor (const segment of baseSegmenter.segment(text)) {\n\t\twhile (marker && segment.index >= marker.end) marker = markers[++markerIndex];\n\t\tif (!marker || segment.index < marker.start) {\n\t\t\tresult.push(segment);\n\t\t} else if (segment.index === marker.start) {\n\t\t\tresult.push({ segment: text.slice(marker.start, marker.end), index: marker.start, input: text });\n\t\t}\n\t}\n\treturn result;\n}\n\nexport class PasteMarkerRegistry {\n\tprivate entries = new Map<number, PasteMarkerEntry>();\n\tprivate pasteCounter = 0;\n\tprivate metadataBuilds = 0;\n\n\tadd(content: string, lineCount: number, charCount: number): string {\n\t\tconst id = ++this.pasteCounter;\n\t\tconst marker = formatPasteMarker(id, lineCount, charCount);\n\t\tthis.entries.set(id, { content, marker, lineCount, charCount });\n\t\tthis.metadataBuilds++;\n\t\treturn marker;\n\t}\n\n\tauthorizedMarkers(text: string): ReadonlySet<string> {\n\t\tconst markers = new Set<string>();\n\t\tfor (const entry of this.entries.values()) {\n\t\t\tif (countOccurrences(text, entry.marker) === 1) markers.add(entry.marker);\n\t\t}\n\t\treturn markers;\n\t}\n\n\tclear(): void {\n\t\tthis.entries.clear();\n\t\tthis.pasteCounter = 0;\n\t}\n\n\texpand(text: string): string {\n\t\treturn expandPasteMarkers(text, this.snapshot());\n\t}\n\n\tget metadataBuildCount(): number {\n\t\treturn this.metadataBuilds;\n\t}\n\n\tinstall(state: EditorPasteState, text: string): void {\n\t\tthis.entries = entriesFromState(state);\n\t\tthis.metadataBuilds += this.entries.size;\n\t\tthis.pasteCounter = Math.max(state.pasteCounter, ...this.entries.keys(), 0);\n\t\tthis.prune(text);\n\t}\n\n\tprune(text: string, previousText?: string): void {\n\t\tfor (const [id, entry] of this.entries) {\n\t\t\tconst wasAuthorized = previousText === undefined || countOccurrences(previousText, entry.marker) === 1;\n\t\t\tif (!wasAuthorized || countOccurrences(text, entry.marker) !== 1) this.entries.delete(id);\n\t\t}\n\t\tthis.pasteCounter = Math.max(...this.entries.keys(), 0);\n\t}\n\n\tremove(id: number, text: string): PasteMarkerRemoval {\n\t\tconst removedEntry = this.entries.get(id);\n\t\tif (!removedEntry || !this.entries.delete(id)) return { text, removed: false };\n\n\t\tlet updatedText = replaceSingleOccurrence(text, removedEntry.marker, \"\");\n\t\tconst higherIds = [...this.entries.keys()].filter((entryId) => entryId > id).sort((a, b) => a - b);\n\t\tfor (const oldId of higherIds) {\n\t\t\tconst entry = this.entries.get(oldId)!;\n\t\t\tthis.entries.delete(oldId);\n\t\t\tif (countOccurrences(updatedText, entry.marker) !== 1) continue;\n\t\t\tconst newId = oldId - 1;\n\t\t\tconst renamed = formatPasteMarker(newId, entry.lineCount, entry.charCount);\n\t\t\tupdatedText = replaceSingleOccurrence(updatedText, entry.marker, renamed);\n\t\t\tthis.entries.set(newId, { ...entry, marker: renamed });\n\t\t}\n\t\tthis.pasteCounter = Math.max(...this.entries.keys(), 0);\n\t\treturn { text: updatedText, removed: true };\n\t}\n\n\tsnapshot(): EditorPasteState {\n\t\tconst pastes = new Map<number, string>();\n\t\tconst markers = new Map<number, string>();\n\t\tfor (const [id, entry] of this.entries) {\n\t\t\tpastes.set(id, entry.content);\n\t\t\tmarkers.set(id, entry.marker);\n\t\t}\n\t\treturn { pastes, markers, pasteCounter: this.pasteCounter };\n\t}\n\n\trestore(state: EditorPasteState): void {\n\t\tthis.entries = entriesFromState(state);\n\t\tthis.metadataBuilds += this.entries.size;\n\t\tthis.pasteCounter = Math.max(state.pasteCounter, ...this.entries.keys(), 0);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type TmuxExecFile } from "./tmux-image-probe.ts";
|
|
2
|
+
export type ImageProtocol = "kitty" | "iterm2" | null;
|
|
3
|
+
export interface TerminalCapabilities {
|
|
4
|
+
images: ImageProtocol;
|
|
5
|
+
trueColor: boolean;
|
|
6
|
+
hyperlinks: boolean;
|
|
7
|
+
tmuxPassthrough?: boolean;
|
|
8
|
+
kittyUnicodePlaceholders?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface DetectedTerminalCapabilities extends TerminalCapabilities {
|
|
11
|
+
cellDimensions?: {
|
|
12
|
+
widthPx: number;
|
|
13
|
+
heightPx: number;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export declare function outerKittyGraphicsMode(clientTermname: string): "placeholder" | null;
|
|
17
|
+
export declare function detectTerminalCapabilities(env?: NodeJS.ProcessEnv, _platform?: NodeJS.Platform, execTmux?: TmuxExecFile): DetectedTerminalCapabilities;
|
|
18
|
+
//# sourceMappingURL=terminal-capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-capabilities.d.ts","sourceRoot":"","sources":["../src/terminal-capabilities.ts"],"names":[],"mappings":"AAKA,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE/E,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACpC,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,4BAA6B,SAAQ,oBAAoB;IACzE,cAAc,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CACvD;AAED,wBAAgB,sBAAsB,CAAC,cAAc,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAGnF;AAED,wBAAgB,0BAA0B,CACzC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,SAAS,GAAE,MAAM,CAAC,QAA2B,EAC7C,QAAQ,CAAC,EAAE,YAAY,GACrB,4BAA4B,CAwD9B"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { decideTmuxImageCapability, parseTmuxKittyTerminalOverride, } from "./tmux-image-capability.js";
|
|
2
|
+
import { probeTmuxImageState } from "./tmux-image-probe.js";
|
|
3
|
+
export function outerKittyGraphicsMode(clientTermname) {
|
|
4
|
+
const term = clientTermname.trim().toLowerCase();
|
|
5
|
+
return term.includes("kitty") || term.includes("ghostty") || term.includes("warp") ? "placeholder" : null;
|
|
6
|
+
}
|
|
7
|
+
export function detectTerminalCapabilities(env = process.env, _platform = process.platform, execTmux) {
|
|
8
|
+
const term = env.TERM ?? "";
|
|
9
|
+
const termProgram = (env.TERM_PROGRAM ?? "").toLowerCase();
|
|
10
|
+
const terminalEmulator = env.TERMINAL_EMULATOR ?? "";
|
|
11
|
+
const colorTerm = env.COLORTERM ?? "";
|
|
12
|
+
const trueColor = colorTerm === "truecolor" || colorTerm === "24bit";
|
|
13
|
+
if (env.TMUX || term.startsWith("tmux")) {
|
|
14
|
+
const state = probeTmuxImageState(env, execTmux);
|
|
15
|
+
const override = parseTmuxKittyTerminalOverride(env.PI_TUI_TMUX_KITTY_TERMINAL);
|
|
16
|
+
const decision = decideTmuxImageCapability(state, override);
|
|
17
|
+
return {
|
|
18
|
+
images: decision.enabled ? "kitty" : null,
|
|
19
|
+
trueColor,
|
|
20
|
+
hyperlinks: state.hyperlinks,
|
|
21
|
+
...(decision.enabled ? { tmuxPassthrough: true } : {}),
|
|
22
|
+
...(decision.enabled && decision.placement === "placeholder" ? { kittyUnicodePlaceholders: true } : {}),
|
|
23
|
+
...(state.cellDimensions ? { cellDimensions: state.cellDimensions } : {}),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (env.KITTY_WINDOW_ID) {
|
|
27
|
+
return { images: "kitty", trueColor: true, hyperlinks: true };
|
|
28
|
+
}
|
|
29
|
+
if (termProgram === "ghostty") {
|
|
30
|
+
return { images: "kitty", trueColor: true, hyperlinks: true, kittyUnicodePlaceholders: true };
|
|
31
|
+
}
|
|
32
|
+
if (env.WARP_SESSION_ID || env.WARP_TERMINAL_SESSION_UUID || termProgram === "warpterminal") {
|
|
33
|
+
return { images: "kitty", trueColor: true, hyperlinks: true };
|
|
34
|
+
}
|
|
35
|
+
if (env.WEZTERM_PANE || termProgram === "wezterm") {
|
|
36
|
+
return { images: "kitty", trueColor: true, hyperlinks: true };
|
|
37
|
+
}
|
|
38
|
+
if (termProgram === "iterm.app" || env.ITERM_SESSION_ID) {
|
|
39
|
+
return { images: "iterm2", trueColor: true, hyperlinks: true };
|
|
40
|
+
}
|
|
41
|
+
if (termProgram === "apple_terminal") {
|
|
42
|
+
return { images: null, trueColor: false, hyperlinks: true };
|
|
43
|
+
}
|
|
44
|
+
if (env.WT_SESSION) {
|
|
45
|
+
return { images: null, trueColor: true, hyperlinks: true };
|
|
46
|
+
}
|
|
47
|
+
if (termProgram === "vscode") {
|
|
48
|
+
return { images: null, trueColor: true, hyperlinks: true };
|
|
49
|
+
}
|
|
50
|
+
if (env.CMUX_WORKSPACE_ID || terminalEmulator.includes("JetBrains")) {
|
|
51
|
+
return { images: null, trueColor: true, hyperlinks: false };
|
|
52
|
+
}
|
|
53
|
+
if (term.startsWith("screen")) {
|
|
54
|
+
return { images: null, trueColor, hyperlinks: false };
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
images: term === "xterm-kitty" ? "kitty" : null,
|
|
58
|
+
trueColor,
|
|
59
|
+
hyperlinks: false,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=terminal-capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-capabilities.js","sourceRoot":"","sources":["../src/terminal-capabilities.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tdecideTmuxImageCapability,\n\tparseTmuxKittyTerminalOverride,\n\ttype TmuxKittyTerminalOverride,\n} from \"./tmux-image-capability.ts\";\nimport { probeTmuxImageState, type TmuxExecFile } from \"./tmux-image-probe.ts\";\n\nexport type ImageProtocol = \"kitty\" | \"iterm2\" | null;\n\nexport interface TerminalCapabilities {\n\timages: ImageProtocol;\n\ttrueColor: boolean;\n\thyperlinks: boolean;\n\ttmuxPassthrough?: boolean;\n\tkittyUnicodePlaceholders?: boolean;\n}\n\nexport interface DetectedTerminalCapabilities extends TerminalCapabilities {\n\tcellDimensions?: { widthPx: number; heightPx: number };\n}\n\nexport function outerKittyGraphicsMode(clientTermname: string): \"placeholder\" | null {\n\tconst term = clientTermname.trim().toLowerCase();\n\treturn term.includes(\"kitty\") || term.includes(\"ghostty\") || term.includes(\"warp\") ? \"placeholder\" : null;\n}\n\nexport function detectTerminalCapabilities(\n\tenv: NodeJS.ProcessEnv = process.env,\n\t_platform: NodeJS.Platform = process.platform,\n\texecTmux?: TmuxExecFile,\n): DetectedTerminalCapabilities {\n\tconst term = env.TERM ?? \"\";\n\tconst termProgram = (env.TERM_PROGRAM ?? \"\").toLowerCase();\n\tconst terminalEmulator = env.TERMINAL_EMULATOR ?? \"\";\n\tconst colorTerm = env.COLORTERM ?? \"\";\n\tconst trueColor = colorTerm === \"truecolor\" || colorTerm === \"24bit\";\n\n\tif (env.TMUX || term.startsWith(\"tmux\")) {\n\t\tconst state = probeTmuxImageState(env, execTmux);\n\t\tconst override: TmuxKittyTerminalOverride = parseTmuxKittyTerminalOverride(env.PI_TUI_TMUX_KITTY_TERMINAL);\n\t\tconst decision = decideTmuxImageCapability(state, override);\n\t\treturn {\n\t\t\timages: decision.enabled ? \"kitty\" : null,\n\t\t\ttrueColor,\n\t\t\thyperlinks: state.hyperlinks,\n\t\t\t...(decision.enabled ? { tmuxPassthrough: true } : {}),\n\t\t\t...(decision.enabled && decision.placement === \"placeholder\" ? { kittyUnicodePlaceholders: true } : {}),\n\t\t\t...(state.cellDimensions ? { cellDimensions: state.cellDimensions } : {}),\n\t\t};\n\t}\n\n\tif (env.KITTY_WINDOW_ID) {\n\t\treturn { images: \"kitty\", trueColor: true, hyperlinks: true };\n\t}\n\tif (termProgram === \"ghostty\") {\n\t\treturn { images: \"kitty\", trueColor: true, hyperlinks: true, kittyUnicodePlaceholders: true };\n\t}\n\tif (env.WARP_SESSION_ID || env.WARP_TERMINAL_SESSION_UUID || termProgram === \"warpterminal\") {\n\t\treturn { images: \"kitty\", trueColor: true, hyperlinks: true };\n\t}\n\tif (env.WEZTERM_PANE || termProgram === \"wezterm\") {\n\t\treturn { images: \"kitty\", trueColor: true, hyperlinks: true };\n\t}\n\tif (termProgram === \"iterm.app\" || env.ITERM_SESSION_ID) {\n\t\treturn { images: \"iterm2\", trueColor: true, hyperlinks: true };\n\t}\n\tif (termProgram === \"apple_terminal\") {\n\t\treturn { images: null, trueColor: false, hyperlinks: true };\n\t}\n\tif (env.WT_SESSION) {\n\t\treturn { images: null, trueColor: true, hyperlinks: true };\n\t}\n\tif (termProgram === \"vscode\") {\n\t\treturn { images: null, trueColor: true, hyperlinks: true };\n\t}\n\tif (env.CMUX_WORKSPACE_ID || terminalEmulator.includes(\"JetBrains\")) {\n\t\treturn { images: null, trueColor: true, hyperlinks: false };\n\t}\n\tif (term.startsWith(\"screen\")) {\n\t\treturn { images: null, trueColor, hyperlinks: false };\n\t}\n\treturn {\n\t\timages: term === \"xterm-kitty\" ? \"kitty\" : null,\n\t\ttrueColor,\n\t\thyperlinks: false,\n\t};\n}\n"]}
|
package/dist/terminal-image.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { type DetectedTerminalCapabilities, type TerminalCapabilities } from "./terminal-capabilities.ts";
|
|
2
|
+
export type { ImageProtocol, TerminalCapabilities } from "./terminal-capabilities.ts";
|
|
3
|
+
export { outerKittyGraphicsMode } from "./terminal-capabilities.ts";
|
|
4
|
+
export interface TmuxPassthroughState {
|
|
5
|
+
allowPassthrough: "off" | "on" | "all";
|
|
6
|
+
clientTermname: string;
|
|
7
|
+
cellWidthPx?: number;
|
|
8
|
+
cellHeightPx?: number;
|
|
6
9
|
}
|
|
7
10
|
export interface CellDimensions {
|
|
8
11
|
widthPx: number;
|
|
@@ -23,11 +26,17 @@ export interface ImageRenderOptions {
|
|
|
23
26
|
}
|
|
24
27
|
export declare function getCellDimensions(): CellDimensions;
|
|
25
28
|
export declare function setCellDimensions(dims: CellDimensions): void;
|
|
26
|
-
export declare function detectCapabilities(tmuxForwardsHyperlink?: () => boolean):
|
|
29
|
+
export declare function detectCapabilities(tmuxForwardsHyperlink?: () => boolean, tmuxPassthroughState?: () => TmuxPassthroughState): DetectedTerminalCapabilities;
|
|
27
30
|
export declare function getCapabilities(): TerminalCapabilities;
|
|
28
31
|
export declare function resetCapabilitiesCache(): void;
|
|
29
32
|
/** Override the cached capabilities. Useful in tests to exercise both code paths. */
|
|
30
33
|
export declare function setCapabilities(caps: TerminalCapabilities): void;
|
|
34
|
+
/**
|
|
35
|
+
* Wrap an escape sequence in a tmux DCS passthrough envelope so tmux forwards
|
|
36
|
+
* it verbatim to the outer terminal (requires `allow-passthrough`). Every ESC
|
|
37
|
+
* inside the payload must be doubled per the tmux protocol.
|
|
38
|
+
*/
|
|
39
|
+
export declare function wrapTmuxPassthrough(sequence: string): string;
|
|
31
40
|
export declare function isImageLine(line: string): boolean;
|
|
32
41
|
/**
|
|
33
42
|
* Generate a random image ID for Kitty graphics protocol.
|
|
@@ -41,7 +50,22 @@ export declare function encodeKitty(base64Data: string, options?: {
|
|
|
41
50
|
imageId?: number;
|
|
42
51
|
/** Whether Kitty should apply its default cursor movement after placement. Default: true. */
|
|
43
52
|
moveCursor?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Create a virtual placement (`U=1`) for Unicode placeholders instead of
|
|
55
|
+
* placing the image at the cursor. The image is only shown where
|
|
56
|
+
* placeholder cells (see {@link buildKittyPlaceholderRow}) are printed.
|
|
57
|
+
*/
|
|
58
|
+
virtual?: boolean;
|
|
44
59
|
}): string;
|
|
60
|
+
/** Maximum rows/columns addressable by Kitty Unicode placeholder diacritics. */
|
|
61
|
+
export declare const KITTY_PLACEHOLDER_MAX: number;
|
|
62
|
+
/**
|
|
63
|
+
* Build one row of Kitty Unicode placeholder cells for a virtual placement.
|
|
64
|
+
* The foreground color carries the low 24 bits of the image ID; the third
|
|
65
|
+
* diacritic carries the high byte when present. Placeholder cells are plain
|
|
66
|
+
* text (1 column each), so tmux and the TUI treat them like any other line.
|
|
67
|
+
*/
|
|
68
|
+
export declare function buildKittyPlaceholderRow(imageId: number, row: number, columns: number): string;
|
|
45
69
|
/**
|
|
46
70
|
* Delete a Kitty graphics image by ID.
|
|
47
71
|
* Uses uppercase 'I' to also free the image data.
|
|
@@ -73,6 +97,7 @@ export declare function renderImage(base64Data: string, imageDimensions: ImageDi
|
|
|
73
97
|
sequence: string;
|
|
74
98
|
rows: number;
|
|
75
99
|
imageId?: number;
|
|
100
|
+
lines?: string[];
|
|
76
101
|
} | null;
|
|
77
102
|
/**
|
|
78
103
|
* Wrap text in an OSC 8 hyperlink sequence.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-image.d.ts","sourceRoot":"","sources":["../src/terminal-image.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"terminal-image.d.ts","sourceRoot":"","sources":["../src/terminal-image.ts"],"names":[],"mappings":"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"}
|
package/dist/terminal-image.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { detectTerminalCapabilities, } from "./terminal-capabilities.js";
|
|
2
|
+
export { outerKittyGraphicsMode } from "./terminal-capabilities.js";
|
|
2
3
|
let cachedCapabilities = null;
|
|
3
4
|
// Default cell dimensions - updated by TUI when terminal responds to query
|
|
4
5
|
let cellDimensions = { widthPx: 9, heightPx: 18 };
|
|
@@ -8,79 +9,33 @@ export function getCellDimensions() {
|
|
|
8
9
|
export function setCellDimensions(dims) {
|
|
9
10
|
cellDimensions = dims;
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
export function detectCapabilities(tmuxForwardsHyperlink = probeTmuxHyperlinks) {
|
|
33
|
-
const termProgram = process.env.TERM_PROGRAM?.toLowerCase() || "";
|
|
34
|
-
const terminalEmulator = process.env.TERMINAL_EMULATOR?.toLowerCase() || "";
|
|
35
|
-
const term = process.env.TERM?.toLowerCase() || "";
|
|
36
|
-
const colorTerm = process.env.COLORTERM?.toLowerCase() || "";
|
|
37
|
-
const hasTrueColorHint = colorTerm === "truecolor" || colorTerm === "24bit";
|
|
38
|
-
// Emit OSC 8 hyperlinks only when tmux confirms it forwards.
|
|
39
|
-
// Image protocols are unreliable under tmux, so leave `images: null`.
|
|
40
|
-
if (process.env.TMUX || term.startsWith("tmux")) {
|
|
41
|
-
return { images: null, trueColor: hasTrueColorHint, hyperlinks: tmuxForwardsHyperlink() };
|
|
42
|
-
}
|
|
43
|
-
// screen does not forward OSC 8 hyperlinks, so keep them off there.
|
|
44
|
-
if (term.startsWith("screen")) {
|
|
45
|
-
return { images: null, trueColor: hasTrueColorHint, hyperlinks: false };
|
|
46
|
-
}
|
|
47
|
-
if (process.env.KITTY_WINDOW_ID || termProgram === "kitty") {
|
|
48
|
-
return { images: "kitty", trueColor: true, hyperlinks: true };
|
|
49
|
-
}
|
|
50
|
-
if (termProgram === "ghostty" || term.includes("ghostty") || process.env.GHOSTTY_RESOURCES_DIR) {
|
|
51
|
-
return { images: "kitty", trueColor: true, hyperlinks: true };
|
|
52
|
-
}
|
|
53
|
-
if (process.env.WEZTERM_PANE || termProgram === "wezterm") {
|
|
54
|
-
return { images: "kitty", trueColor: true, hyperlinks: true };
|
|
55
|
-
}
|
|
56
|
-
// Warp supports the Kitty graphics protocol and OSC 8 hyperlinks.
|
|
57
|
-
if (termProgram === "warpterminal" || process.env.WARP_SESSION_ID || process.env.WARP_TERMINAL_SESSION_UUID) {
|
|
58
|
-
return { images: "kitty", trueColor: true, hyperlinks: true };
|
|
59
|
-
}
|
|
60
|
-
if (process.env.ITERM_SESSION_ID || termProgram === "iterm.app") {
|
|
61
|
-
return { images: "iterm2", trueColor: true, hyperlinks: true };
|
|
62
|
-
}
|
|
63
|
-
if (process.env.WT_SESSION) {
|
|
64
|
-
return { images: null, trueColor: true, hyperlinks: true };
|
|
65
|
-
}
|
|
66
|
-
if (termProgram === "vscode") {
|
|
67
|
-
return { images: null, trueColor: true, hyperlinks: true };
|
|
68
|
-
}
|
|
69
|
-
if (termProgram === "alacritty") {
|
|
70
|
-
return { images: null, trueColor: true, hyperlinks: true };
|
|
71
|
-
}
|
|
72
|
-
if (terminalEmulator === "jetbrains-jediterm") {
|
|
73
|
-
return { images: null, trueColor: true, hyperlinks: false };
|
|
74
|
-
}
|
|
75
|
-
// Unknown terminal: be conservative. OSC 8 is rendered invisibly as "just
|
|
76
|
-
// text" on terminals that swallow it, which means the URL disappears from
|
|
77
|
-
// the rendered output. Default to the legacy `text (url)` behavior unless we
|
|
78
|
-
// have positively identified a hyperlink-capable terminal above.
|
|
79
|
-
return { images: null, trueColor: hasTrueColorHint, hyperlinks: false };
|
|
12
|
+
export function detectCapabilities(tmuxForwardsHyperlink, tmuxPassthroughState) {
|
|
13
|
+
if (!tmuxForwardsHyperlink || !tmuxPassthroughState)
|
|
14
|
+
return detectTerminalCapabilities();
|
|
15
|
+
const legacy = tmuxPassthroughState();
|
|
16
|
+
const hyperlinks = tmuxForwardsHyperlink();
|
|
17
|
+
const detected = detectTerminalCapabilities({ ...process.env, TMUX: process.env.TMUX ?? "compat,0,0" }, process.platform, () => [
|
|
18
|
+
"3.4",
|
|
19
|
+
legacy.allowPassthrough,
|
|
20
|
+
"on",
|
|
21
|
+
"1",
|
|
22
|
+
"1",
|
|
23
|
+
"1",
|
|
24
|
+
legacy.clientTermname,
|
|
25
|
+
legacy.cellWidthPx ?? "",
|
|
26
|
+
legacy.cellHeightPx ?? "",
|
|
27
|
+
hyperlinks ? "hyperlinks" : "",
|
|
28
|
+
].join("|"));
|
|
29
|
+
if (detected.cellDimensions)
|
|
30
|
+
setCellDimensions(detected.cellDimensions);
|
|
31
|
+
return { ...detected, hyperlinks };
|
|
80
32
|
}
|
|
81
33
|
export function getCapabilities() {
|
|
82
34
|
if (!cachedCapabilities) {
|
|
83
|
-
|
|
35
|
+
const detected = detectCapabilities();
|
|
36
|
+
if (detected.cellDimensions)
|
|
37
|
+
setCellDimensions(detected.cellDimensions);
|
|
38
|
+
cachedCapabilities = detected;
|
|
84
39
|
}
|
|
85
40
|
return cachedCapabilities;
|
|
86
41
|
}
|
|
@@ -93,6 +48,14 @@ export function setCapabilities(caps) {
|
|
|
93
48
|
}
|
|
94
49
|
const KITTY_PREFIX = "\x1b_G";
|
|
95
50
|
const ITERM2_PREFIX = "\x1b]1337;File=";
|
|
51
|
+
/**
|
|
52
|
+
* Wrap an escape sequence in a tmux DCS passthrough envelope so tmux forwards
|
|
53
|
+
* it verbatim to the outer terminal (requires `allow-passthrough`). Every ESC
|
|
54
|
+
* inside the payload must be doubled per the tmux protocol.
|
|
55
|
+
*/
|
|
56
|
+
export function wrapTmuxPassthrough(sequence) {
|
|
57
|
+
return `\x1bPtmux;${sequence.replaceAll("\x1b", "\x1b\x1b")}\x1b\\`;
|
|
58
|
+
}
|
|
96
59
|
export function isImageLine(line) {
|
|
97
60
|
// Fast path: sequence at line start (single-row images)
|
|
98
61
|
if (line.startsWith(KITTY_PREFIX) || line.startsWith(ITERM2_PREFIX)) {
|
|
@@ -113,7 +76,9 @@ export function allocateImageId() {
|
|
|
113
76
|
export function encodeKitty(base64Data, options = {}) {
|
|
114
77
|
const CHUNK_SIZE = 4096;
|
|
115
78
|
const params = ["a=T", "f=100", "q=2"];
|
|
116
|
-
if (options.
|
|
79
|
+
if (options.virtual)
|
|
80
|
+
params.push("U=1");
|
|
81
|
+
else if (options.moveCursor === false)
|
|
117
82
|
params.push("C=1");
|
|
118
83
|
if (options.columns)
|
|
119
84
|
params.push(`c=${options.columns}`);
|
|
@@ -121,42 +86,107 @@ export function encodeKitty(base64Data, options = {}) {
|
|
|
121
86
|
params.push(`r=${options.rows}`);
|
|
122
87
|
if (options.imageId)
|
|
123
88
|
params.push(`i=${options.imageId}`);
|
|
124
|
-
if (base64Data.length <= CHUNK_SIZE) {
|
|
125
|
-
return `\x1b_G${params.join(",")};${base64Data}\x1b\\`;
|
|
126
|
-
}
|
|
127
89
|
const chunks = [];
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
90
|
+
if (base64Data.length <= CHUNK_SIZE) {
|
|
91
|
+
chunks.push(`\x1b_G${params.join(",")};${base64Data}\x1b\\`);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
let offset = 0;
|
|
95
|
+
let isFirst = true;
|
|
96
|
+
while (offset < base64Data.length) {
|
|
97
|
+
const chunk = base64Data.slice(offset, offset + CHUNK_SIZE);
|
|
98
|
+
const isLast = offset + CHUNK_SIZE >= base64Data.length;
|
|
99
|
+
if (isFirst) {
|
|
100
|
+
chunks.push(`\x1b_G${params.join(",")},m=1;${chunk}\x1b\\`);
|
|
101
|
+
isFirst = false;
|
|
102
|
+
}
|
|
103
|
+
else if (isLast) {
|
|
104
|
+
chunks.push(`\x1b_Gm=0;${chunk}\x1b\\`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
chunks.push(`\x1b_Gm=1;${chunk}\x1b\\`);
|
|
108
|
+
}
|
|
109
|
+
offset += CHUNK_SIZE;
|
|
142
110
|
}
|
|
143
|
-
|
|
111
|
+
}
|
|
112
|
+
// Under tmux passthrough every APC chunk must be wrapped individually so
|
|
113
|
+
// tmux forwards each one as its own DCS envelope.
|
|
114
|
+
if (getCapabilities().tmuxPassthrough) {
|
|
115
|
+
return chunks.map(wrapTmuxPassthrough).join("");
|
|
144
116
|
}
|
|
145
117
|
return chunks.join("");
|
|
146
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Placeholder codepoint for Kitty Unicode placements (U+10EEEE). Each cell of
|
|
121
|
+
* a virtual placement is this character with combining diacritics encoding the
|
|
122
|
+
* row/column and the foreground color encoding the image ID.
|
|
123
|
+
*/
|
|
124
|
+
const KITTY_PLACEHOLDER = String.fromCodePoint(0x10eeee);
|
|
125
|
+
/**
|
|
126
|
+
* Row/column index diacritics from the Kitty graphics protocol
|
|
127
|
+
* (rowcolumn-diacritics.txt): diacritic at index N encodes row/column N.
|
|
128
|
+
*/
|
|
129
|
+
const KITTY_PLACEHOLDER_DIACRITICS = [
|
|
130
|
+
0x0305, 0x030d, 0x030e, 0x0310, 0x0312, 0x033d, 0x033e, 0x033f, 0x0346, 0x034a, 0x034b, 0x034c, 0x0350, 0x0351,
|
|
131
|
+
0x0352, 0x0357, 0x035b, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d,
|
|
132
|
+
0x036e, 0x036f, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0592, 0x0593, 0x0594, 0x0595, 0x0597, 0x0598, 0x0599,
|
|
133
|
+
0x059c, 0x059d, 0x059e, 0x059f, 0x05a0, 0x05a1, 0x05a8, 0x05a9, 0x05ab, 0x05ac, 0x05af, 0x05c4, 0x0610, 0x0611,
|
|
134
|
+
0x0612, 0x0613, 0x0614, 0x0615, 0x0616, 0x0617, 0x0657, 0x0658, 0x0659, 0x065a, 0x065b, 0x065d, 0x065e, 0x06d6,
|
|
135
|
+
0x06d7, 0x06d8, 0x06d9, 0x06da, 0x06db, 0x06dc, 0x06df, 0x06e0, 0x06e1, 0x06e2, 0x06e4, 0x06e7, 0x06e8, 0x06eb,
|
|
136
|
+
0x06ec, 0x0730, 0x0732, 0x0733, 0x0735, 0x0736, 0x073a, 0x073d, 0x073f, 0x0740, 0x0741, 0x0743, 0x0745, 0x0747,
|
|
137
|
+
0x0749, 0x074a, 0x07eb, 0x07ec, 0x07ed, 0x07ee, 0x07ef, 0x07f0, 0x07f1, 0x07f3, 0x0816, 0x0817, 0x0818, 0x0819,
|
|
138
|
+
0x081b, 0x081c, 0x081d, 0x081e, 0x081f, 0x0820, 0x0821, 0x0822, 0x0823, 0x0825, 0x0826, 0x0827, 0x0829, 0x082a,
|
|
139
|
+
0x082b, 0x082c, 0x082d, 0x0951, 0x0953, 0x0954, 0x0f82, 0x0f83, 0x0f86, 0x0f87, 0x135d, 0x135e, 0x135f, 0x17dd,
|
|
140
|
+
0x193a, 0x1a17, 0x1a75, 0x1a76, 0x1a77, 0x1a78, 0x1a79, 0x1a7a, 0x1a7b, 0x1a7c, 0x1b6b, 0x1b6d, 0x1b6e, 0x1b6f,
|
|
141
|
+
0x1b70, 0x1b71, 0x1b72, 0x1b73, 0x1cd0, 0x1cd1, 0x1cd2, 0x1cda, 0x1cdb, 0x1ce0, 0x1dc0, 0x1dc1, 0x1dc3, 0x1dc4,
|
|
142
|
+
0x1dc5, 0x1dc6, 0x1dc7, 0x1dc8, 0x1dc9, 0x1dcb, 0x1dcc, 0x1dd1, 0x1dd2, 0x1dd3, 0x1dd4, 0x1dd5, 0x1dd6, 0x1dd7,
|
|
143
|
+
0x1dd8, 0x1dd9, 0x1dda, 0x1ddb, 0x1ddc, 0x1ddd, 0x1dde, 0x1ddf, 0x1de0, 0x1de1, 0x1de2, 0x1de3, 0x1de4, 0x1de5,
|
|
144
|
+
0x1de6, 0x1dfe, 0x20d0, 0x20d1, 0x20d4, 0x20d5, 0x20d6, 0x20d7, 0x20db, 0x20dc, 0x20e1, 0x20e7, 0x20e9, 0x20f0,
|
|
145
|
+
0x2cef, 0x2cf0, 0x2cf1, 0x2de0, 0x2de1, 0x2de2, 0x2de3, 0x2de4, 0x2de5, 0x2de6, 0x2de7, 0x2de8, 0x2de9, 0x2dea,
|
|
146
|
+
0x2deb, 0x2dec, 0x2ded, 0x2dee, 0x2def, 0x2df0, 0x2df1, 0x2df2, 0x2df3, 0x2df4, 0x2df5, 0x2df6, 0x2df7, 0x2df8,
|
|
147
|
+
0x2df9, 0x2dfa, 0x2dfb, 0x2dfc, 0x2dfd, 0x2dfe, 0x2dff, 0xa66f, 0xa67c, 0xa67d, 0xa6f0, 0xa6f1, 0xa8e0, 0xa8e1,
|
|
148
|
+
0xa8e2, 0xa8e3, 0xa8e4, 0xa8e5, 0xa8e6, 0xa8e7, 0xa8e8, 0xa8e9, 0xa8ea, 0xa8eb, 0xa8ec, 0xa8ed, 0xa8ee, 0xa8ef,
|
|
149
|
+
0xa8f0, 0xa8f1, 0xaab0, 0xaab2, 0xaab3, 0xaab7, 0xaab8, 0xaabe, 0xaabf, 0xaac1, 0xfe20, 0xfe21, 0xfe22, 0xfe23,
|
|
150
|
+
0xfe24, 0xfe25, 0xfe26, 0x10a0f, 0x10a38, 0x1d185, 0x1d186, 0x1d187, 0x1d188, 0x1d189, 0x1d1aa, 0x1d1ab, 0x1d1ac,
|
|
151
|
+
0x1d1ad, 0x1d242, 0x1d243, 0x1d244,
|
|
152
|
+
];
|
|
153
|
+
/** Maximum rows/columns addressable by Kitty Unicode placeholder diacritics. */
|
|
154
|
+
export const KITTY_PLACEHOLDER_MAX = KITTY_PLACEHOLDER_DIACRITICS.length;
|
|
155
|
+
/**
|
|
156
|
+
* Build one row of Kitty Unicode placeholder cells for a virtual placement.
|
|
157
|
+
* The foreground color carries the low 24 bits of the image ID; the third
|
|
158
|
+
* diacritic carries the high byte when present. Placeholder cells are plain
|
|
159
|
+
* text (1 column each), so tmux and the TUI treat them like any other line.
|
|
160
|
+
*/
|
|
161
|
+
export function buildKittyPlaceholderRow(imageId, row, columns) {
|
|
162
|
+
const rowDiacritic = String.fromCodePoint(KITTY_PLACEHOLDER_DIACRITICS[Math.min(row, KITTY_PLACEHOLDER_MAX - 1)]);
|
|
163
|
+
const idHighByte = (imageId >>> 24) & 0xff;
|
|
164
|
+
const idDiacritic = idHighByte > 0 ? String.fromCodePoint(KITTY_PLACEHOLDER_DIACRITICS[idHighByte]) : "";
|
|
165
|
+
const r = (imageId >>> 16) & 0xff;
|
|
166
|
+
const g = (imageId >>> 8) & 0xff;
|
|
167
|
+
const b = imageId & 0xff;
|
|
168
|
+
let cells = "";
|
|
169
|
+
for (let column = 0; column < columns; column++) {
|
|
170
|
+
const columnDiacritic = String.fromCodePoint(KITTY_PLACEHOLDER_DIACRITICS[Math.min(column, KITTY_PLACEHOLDER_MAX - 1)]);
|
|
171
|
+
cells += KITTY_PLACEHOLDER + rowDiacritic + columnDiacritic + idDiacritic;
|
|
172
|
+
}
|
|
173
|
+
return `\x1b[38;2;${r};${g};${b}m${cells}\x1b[39m`;
|
|
174
|
+
}
|
|
147
175
|
/**
|
|
148
176
|
* Delete a Kitty graphics image by ID.
|
|
149
177
|
* Uses uppercase 'I' to also free the image data.
|
|
150
178
|
*/
|
|
151
179
|
export function deleteKittyImage(imageId) {
|
|
152
|
-
|
|
180
|
+
const sequence = `\x1b_Ga=d,d=I,i=${imageId},q=2\x1b\\`;
|
|
181
|
+
return getCapabilities().tmuxPassthrough ? wrapTmuxPassthrough(sequence) : sequence;
|
|
153
182
|
}
|
|
154
183
|
/**
|
|
155
184
|
* Delete all visible Kitty graphics images.
|
|
156
185
|
* Uses uppercase 'A' to also free the image data.
|
|
157
186
|
*/
|
|
158
187
|
export function deleteAllKittyImages() {
|
|
159
|
-
|
|
188
|
+
const sequence = "\x1b_Ga=d,d=A,q=2\x1b\\";
|
|
189
|
+
return getCapabilities().tmuxPassthrough ? wrapTmuxPassthrough(sequence) : sequence;
|
|
160
190
|
}
|
|
161
191
|
export function encodeITerm2(base64Data, options = {}) {
|
|
162
192
|
const params = [`inline=${options.inline !== false ? 1 : 0}`];
|
|
@@ -323,6 +353,21 @@ export function renderImage(base64Data, imageDimensions, options = {}) {
|
|
|
323
353
|
}
|
|
324
354
|
const maxWidth = options.maxWidthCells ?? 80;
|
|
325
355
|
const size = calculateImageCellSize(imageDimensions, maxWidth, options.maxHeightCells, getCellDimensions());
|
|
356
|
+
if (caps.images === "kitty" && caps.kittyUnicodePlaceholders) {
|
|
357
|
+
// Virtual placement: transmit once, then show the image with plain-text
|
|
358
|
+
// placeholder cells. Under tmux the placeholder cells move/clip with the
|
|
359
|
+
// pane, so split layouts render correctly.
|
|
360
|
+
const imageId = options.imageId ?? allocateImageId();
|
|
361
|
+
const columns = Math.min(size.columns, KITTY_PLACEHOLDER_MAX);
|
|
362
|
+
const rows = Math.min(size.rows, KITTY_PLACEHOLDER_MAX);
|
|
363
|
+
const sequence = encodeKitty(base64Data, { columns, rows, imageId, virtual: true });
|
|
364
|
+
const lines = [];
|
|
365
|
+
for (let row = 0; row < rows; row++) {
|
|
366
|
+
const placeholderRow = buildKittyPlaceholderRow(imageId, row, columns);
|
|
367
|
+
lines.push(row === 0 ? sequence + placeholderRow : placeholderRow);
|
|
368
|
+
}
|
|
369
|
+
return { sequence, rows, imageId, lines };
|
|
370
|
+
}
|
|
326
371
|
if (caps.images === "kitty") {
|
|
327
372
|
const sequence = encodeKitty(base64Data, {
|
|
328
373
|
columns: size.columns,
|