drexler 0.2.13 → 0.2.15
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 +8 -0
- package/README.md +64 -13
- package/package.json +1 -1
- package/src/commands.ts +127 -20
- package/src/config.ts +141 -32
- package/src/conversation.ts +0 -4
- package/src/index.ts +69 -6
- package/src/pet/petState.ts +408 -0
- package/src/repl.ts +1 -1
- package/src/ui/App.tsx +557 -146
- package/src/ui/CommandPalette.tsx +2 -0
- package/src/ui/DealDeskHeader.tsx +245 -77
- package/src/ui/DeathScreen.tsx +110 -0
- package/src/ui/MarkdownBody.tsx +406 -0
- package/src/ui/MascotIntro.tsx +713 -111
- package/src/ui/Message.tsx +24 -114
- package/src/ui/PetPanel.tsx +537 -0
- package/src/ui/SynergyEvent.tsx +3 -2
- package/src/ui/TranscriptViewport.tsx +461 -70
- package/src/ui/displayContent.ts +117 -0
- package/src/ui/graphemes.ts +1 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const FENCE_OPEN_RE = /^[ \t]*(`{3,}|~{3,})([^`~\n\r]*)$/u;
|
|
2
|
+
const TAB_DISPLAY = " ";
|
|
3
|
+
|
|
4
|
+
export interface AssistantDisplayLine {
|
|
5
|
+
kind: "text" | "code";
|
|
6
|
+
text: string;
|
|
7
|
+
language?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isFenceClose(line: string, fenceChar: string, minLength: number): boolean {
|
|
11
|
+
const trimmed = line.trim();
|
|
12
|
+
if (trimmed.length < minLength) return false;
|
|
13
|
+
for (const char of trimmed) {
|
|
14
|
+
if (char !== fenceChar) return false;
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isMarkdownFence(info: string): boolean {
|
|
20
|
+
const lang = info.trim().split(/\s+/u)[0]?.toLowerCase() ?? "";
|
|
21
|
+
return lang === "markdown" || lang === "md" || lang === "mdown";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function fenceLanguage(info: string): string | undefined {
|
|
25
|
+
const lang = info.trim().split(/\s+/u)[0]?.toLowerCase();
|
|
26
|
+
return lang && !isMarkdownFence(lang) ? lang : undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function assistantDisplayLines(content: string): AssistantDisplayLine[] {
|
|
30
|
+
const lines = content.replace(/\r\n?/gu, "\n").split("\n");
|
|
31
|
+
const output: AssistantDisplayLine[] = [];
|
|
32
|
+
let fenceChar = "";
|
|
33
|
+
let fenceLength = 0;
|
|
34
|
+
let fenceKind: AssistantDisplayLine["kind"] = "text";
|
|
35
|
+
let fenceLang: string | undefined;
|
|
36
|
+
|
|
37
|
+
for (const rawLine of lines) {
|
|
38
|
+
const line = rawLine.replace(/\t/gu, TAB_DISPLAY);
|
|
39
|
+
if (fenceChar.length > 0) {
|
|
40
|
+
if (isFenceClose(line, fenceChar, fenceLength)) {
|
|
41
|
+
fenceChar = "";
|
|
42
|
+
fenceLength = 0;
|
|
43
|
+
fenceKind = "text";
|
|
44
|
+
fenceLang = undefined;
|
|
45
|
+
} else {
|
|
46
|
+
output.push({ kind: fenceKind, text: line, language: fenceLang });
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const openingFence = FENCE_OPEN_RE.exec(line);
|
|
52
|
+
if (openingFence) {
|
|
53
|
+
const marker = openingFence[1]!;
|
|
54
|
+
const info = openingFence[2] ?? "";
|
|
55
|
+
fenceChar = marker[0]!;
|
|
56
|
+
fenceLength = marker.length;
|
|
57
|
+
fenceKind = isMarkdownFence(info) ? "text" : "code";
|
|
58
|
+
fenceLang = fenceKind === "code" ? fenceLanguage(info) : undefined;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
output.push({ kind: "text", text: line });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return output;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function normalizeAssistantDisplayContent(content: string): string {
|
|
69
|
+
return assistantDisplayLines(content)
|
|
70
|
+
.map((line) => line.text)
|
|
71
|
+
.join("\n");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function normalizeAssistantMarkdownRenderContent(content: string): string {
|
|
75
|
+
const lines = content.replace(/\r\n?/gu, "\n").split("\n");
|
|
76
|
+
const output: string[] = [];
|
|
77
|
+
let fenceChar = "";
|
|
78
|
+
let fenceLength = 0;
|
|
79
|
+
let fenceMarker = "";
|
|
80
|
+
let markdownFence = false;
|
|
81
|
+
|
|
82
|
+
for (const rawLine of lines) {
|
|
83
|
+
const line = rawLine.replace(/\t/gu, TAB_DISPLAY);
|
|
84
|
+
if (fenceChar.length > 0) {
|
|
85
|
+
if (isFenceClose(line, fenceChar, fenceLength)) {
|
|
86
|
+
if (!markdownFence) output.push(fenceMarker);
|
|
87
|
+
fenceChar = "";
|
|
88
|
+
fenceLength = 0;
|
|
89
|
+
fenceMarker = "";
|
|
90
|
+
markdownFence = false;
|
|
91
|
+
} else {
|
|
92
|
+
output.push(line);
|
|
93
|
+
}
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const openingFence = FENCE_OPEN_RE.exec(line);
|
|
98
|
+
if (openingFence) {
|
|
99
|
+
const marker = openingFence[1]!;
|
|
100
|
+
const info = openingFence[2] ?? "";
|
|
101
|
+
fenceChar = marker[0]!;
|
|
102
|
+
fenceLength = marker.length;
|
|
103
|
+
fenceMarker = marker;
|
|
104
|
+
markdownFence = isMarkdownFence(info);
|
|
105
|
+
if (!markdownFence) output.push(marker);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
output.push(line);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return output.join("\n");
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function firstDisplayLine(content: string): string {
|
|
116
|
+
return content.split("\n").find((line) => line.trim().length > 0) ?? "";
|
|
117
|
+
}
|
package/src/ui/graphemes.ts
CHANGED
|
@@ -35,6 +35,7 @@ function isWideCodePoint(codePoint: number): boolean {
|
|
|
35
35
|
function graphemeWidth(input: string): number {
|
|
36
36
|
if (input.length === 0) return 0;
|
|
37
37
|
if (/^\p{Mark}+$/u.test(input)) return 0;
|
|
38
|
+
if (/^[©®™]$/u.test(input)) return 1;
|
|
38
39
|
if (/\p{Extended_Pictographic}/u.test(input)) return 2;
|
|
39
40
|
let width = 0;
|
|
40
41
|
for (const char of input) {
|