pi-studio 0.6.0 → 0.6.2
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 +16 -0
- package/README.md +3 -1
- package/client/studio-client.js +123 -5
- package/client/studio.css +206 -98
- package/index.ts +350 -64
- package/package.json +8 -2
- package/themes/pi-studio-dark.json +78 -0
- package/themes/pi-studio-light.json +77 -0
package/index.ts
CHANGED
|
@@ -593,6 +593,8 @@ interface StudioPalette {
|
|
|
593
593
|
interface StudioThemeStyle {
|
|
594
594
|
mode: StudioThemeMode;
|
|
595
595
|
palette: StudioPalette;
|
|
596
|
+
accentContrast?: string;
|
|
597
|
+
errorContrast?: string;
|
|
596
598
|
}
|
|
597
599
|
|
|
598
600
|
const DARK_STUDIO_PALETTE: StudioPalette = {
|
|
@@ -673,9 +675,39 @@ const LIGHT_STUDIO_PALETTE: StudioPalette = {
|
|
|
673
675
|
syntaxPunctuation: "#000000",
|
|
674
676
|
};
|
|
675
677
|
|
|
678
|
+
function inferThemeModeFromName(name: string): StudioThemeMode | undefined {
|
|
679
|
+
const lower = name.toLowerCase();
|
|
680
|
+
if (/\b(light|dawn|day|latte)\b/.test(lower) || lower.includes("-light")) return "light";
|
|
681
|
+
if (/\b(dark|night|moon|mocha)\b/.test(lower) || lower.includes("-dark")) return "dark";
|
|
682
|
+
return undefined;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
function inferThemeModeFromColorCandidates(...colors: Array<string | undefined>): StudioThemeMode | undefined {
|
|
686
|
+
for (const color of colors) {
|
|
687
|
+
const inferred = inferThemeModeFromColor(color);
|
|
688
|
+
if (inferred) return inferred;
|
|
689
|
+
}
|
|
690
|
+
return undefined;
|
|
691
|
+
}
|
|
692
|
+
|
|
676
693
|
function getStudioThemeMode(theme?: Theme): StudioThemeMode {
|
|
677
|
-
const
|
|
678
|
-
|
|
694
|
+
const exported = readThemeExportPalette(theme);
|
|
695
|
+
const inferredFromExport = inferThemeModeFromColorCandidates(exported?.pageBg, exported?.cardBg);
|
|
696
|
+
if (inferredFromExport) return inferredFromExport;
|
|
697
|
+
|
|
698
|
+
const inferredFromSurface = inferThemeModeFromColorCandidates(
|
|
699
|
+
inferThemeSurfaceColor(theme, "page"),
|
|
700
|
+
inferThemeSurfaceColor(theme, "card"),
|
|
701
|
+
readThemeColorToken(theme, "userMessageBg"),
|
|
702
|
+
readThemeColorToken(theme, "customMessageBg"),
|
|
703
|
+
readThemeColorToken(theme, "toolPendingBg"),
|
|
704
|
+
);
|
|
705
|
+
if (inferredFromSurface) return inferredFromSurface;
|
|
706
|
+
|
|
707
|
+
const inferredFromName = inferThemeModeFromName(theme?.name ?? "");
|
|
708
|
+
if (inferredFromName) return inferredFromName;
|
|
709
|
+
|
|
710
|
+
return "dark";
|
|
679
711
|
}
|
|
680
712
|
|
|
681
713
|
function toHexByte(value: number): string {
|
|
@@ -809,6 +841,49 @@ function blendColors(a: string, b: string, t: number): string {
|
|
|
809
841
|
);
|
|
810
842
|
}
|
|
811
843
|
|
|
844
|
+
function wcagRelativeLuminance(color: string): number {
|
|
845
|
+
const rgb = hexToRgb(color);
|
|
846
|
+
if (!rgb) return 0;
|
|
847
|
+
const linear = [rgb.r, rgb.g, rgb.b].map((channel) => {
|
|
848
|
+
const value = channel / 255;
|
|
849
|
+
return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
|
|
850
|
+
});
|
|
851
|
+
return 0.2126 * linear[0]! + 0.7152 * linear[1]! + 0.0722 * linear[2]!;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
function contrastRatio(a: string, b: string): number {
|
|
855
|
+
const lumA = wcagRelativeLuminance(a);
|
|
856
|
+
const lumB = wcagRelativeLuminance(b);
|
|
857
|
+
const lighter = Math.max(lumA, lumB);
|
|
858
|
+
const darker = Math.min(lumA, lumB);
|
|
859
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function readableTextOn(background: string, darkText = "#0e1616", lightText = "#ffffff"): string {
|
|
863
|
+
if (!hexToRgb(background)) return lightText;
|
|
864
|
+
return contrastRatio(background, darkText) >= contrastRatio(background, lightText) ? darkText : lightText;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function capBorderContrast(color: string, surface: string, maxContrast: number): string {
|
|
868
|
+
if (!hexToRgb(color) || !hexToRgb(surface)) return color;
|
|
869
|
+
if (contrastRatio(color, surface) <= maxContrast) return color;
|
|
870
|
+
|
|
871
|
+
let low = 0;
|
|
872
|
+
let high = 1;
|
|
873
|
+
let result = color;
|
|
874
|
+
for (let i = 0; i < 12; i += 1) {
|
|
875
|
+
const mid = (low + high) / 2;
|
|
876
|
+
const candidate = blendColors(color, surface, mid);
|
|
877
|
+
if (contrastRatio(candidate, surface) > maxContrast) {
|
|
878
|
+
low = mid;
|
|
879
|
+
} else {
|
|
880
|
+
result = candidate;
|
|
881
|
+
high = mid;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return result;
|
|
885
|
+
}
|
|
886
|
+
|
|
812
887
|
function deriveCanvasColors(
|
|
813
888
|
baseColor: string,
|
|
814
889
|
mode: StudioThemeMode,
|
|
@@ -848,7 +923,17 @@ interface ThemeExportPalette {
|
|
|
848
923
|
infoBg?: string;
|
|
849
924
|
}
|
|
850
925
|
|
|
851
|
-
|
|
926
|
+
interface ThemeSourceJson {
|
|
927
|
+
name?: string;
|
|
928
|
+
vars?: Record<string, string | number>;
|
|
929
|
+
colors?: Record<string, string | number>;
|
|
930
|
+
export?: { pageBg?: string | number; cardBg?: string | number; infoBg?: string | number };
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
const themeSourceJsonCache = new Map<string, { mtimeMs: number; json: ThemeSourceJson | null }>();
|
|
934
|
+
|
|
935
|
+
const DEFAULT_UI_FONT_STACK = "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif";
|
|
936
|
+
const DEFAULT_PROSE_FONT_STACK = DEFAULT_UI_FONT_STACK;
|
|
852
937
|
|
|
853
938
|
const DEFAULT_MONO_FONT_FAMILIES = [
|
|
854
939
|
"ui-monospace",
|
|
@@ -1049,6 +1134,14 @@ function getStudioMonoFontStack(): string {
|
|
|
1049
1134
|
return cachedStudioMonoFontStack;
|
|
1050
1135
|
}
|
|
1051
1136
|
|
|
1137
|
+
function getStudioUiFontStack(): string {
|
|
1138
|
+
return sanitizeCssValue(process.env.PI_STUDIO_FONT_UI ?? "") || DEFAULT_UI_FONT_STACK;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
function getStudioProseFontStack(): string {
|
|
1142
|
+
return sanitizeCssValue(process.env.PI_STUDIO_FONT_PROSE ?? "") || DEFAULT_PROSE_FONT_STACK;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1052
1145
|
function resolveThemeExportValue(
|
|
1053
1146
|
value: string | number | undefined,
|
|
1054
1147
|
vars: Record<string, string | number>,
|
|
@@ -1071,37 +1164,109 @@ function resolveThemeExportValue(
|
|
|
1071
1164
|
return resolveThemeExportValue(referenced, vars, seen) ?? token;
|
|
1072
1165
|
}
|
|
1073
1166
|
|
|
1074
|
-
function
|
|
1167
|
+
function isCssColorValue(value: string | undefined): value is string {
|
|
1168
|
+
if (!value) return false;
|
|
1169
|
+
const trimmed = value.trim();
|
|
1170
|
+
return /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(trimmed) || /^rgba?\(/i.test(trimmed);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
function normalizeResolvedThemeColor(value: string | undefined): string | undefined {
|
|
1174
|
+
if (!isCssColorValue(value)) return undefined;
|
|
1175
|
+
return value.trim();
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
function readThemeSourceJson(theme?: Theme): ThemeSourceJson | undefined {
|
|
1075
1179
|
const sourcePath = theme?.sourcePath?.trim();
|
|
1076
1180
|
if (!sourcePath) return undefined;
|
|
1077
1181
|
|
|
1078
|
-
if (themeExportPaletteCache.has(sourcePath)) {
|
|
1079
|
-
const cached = themeExportPaletteCache.get(sourcePath);
|
|
1080
|
-
return cached ?? undefined;
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
1182
|
try {
|
|
1084
|
-
const
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
vars?: Record<string, string | number>;
|
|
1088
|
-
};
|
|
1089
|
-
const vars = parsed.vars ?? {};
|
|
1090
|
-
const exportSection = parsed.export ?? {};
|
|
1091
|
-
const resolved: ThemeExportPalette = {
|
|
1092
|
-
pageBg: resolveThemeExportValue(exportSection.pageBg, vars),
|
|
1093
|
-
cardBg: resolveThemeExportValue(exportSection.cardBg, vars),
|
|
1094
|
-
infoBg: resolveThemeExportValue(exportSection.infoBg, vars),
|
|
1095
|
-
};
|
|
1183
|
+
const mtimeMs = statSync(sourcePath).mtimeMs;
|
|
1184
|
+
const cached = themeSourceJsonCache.get(sourcePath);
|
|
1185
|
+
if (cached && cached.mtimeMs === mtimeMs) return cached.json ?? undefined;
|
|
1096
1186
|
|
|
1097
|
-
|
|
1098
|
-
|
|
1187
|
+
const raw = readFileSync(sourcePath, "utf-8");
|
|
1188
|
+
const parsed = JSON.parse(raw) as ThemeSourceJson;
|
|
1189
|
+
themeSourceJsonCache.set(sourcePath, { mtimeMs, json: parsed });
|
|
1190
|
+
return parsed;
|
|
1099
1191
|
} catch {
|
|
1100
|
-
|
|
1192
|
+
themeSourceJsonCache.set(sourcePath, { mtimeMs: -1, json: null });
|
|
1101
1193
|
return undefined;
|
|
1102
1194
|
}
|
|
1103
1195
|
}
|
|
1104
1196
|
|
|
1197
|
+
function resolveThemeJsonValue(
|
|
1198
|
+
value: string | number | undefined,
|
|
1199
|
+
vars: Record<string, string | number>,
|
|
1200
|
+
): string | undefined {
|
|
1201
|
+
return normalizeResolvedThemeColor(resolveThemeExportValue(value, vars));
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
function readThemeExportPalette(theme?: Theme): ThemeExportPalette | undefined {
|
|
1205
|
+
const parsed = readThemeSourceJson(theme);
|
|
1206
|
+
if (!parsed) return undefined;
|
|
1207
|
+
const vars = parsed.vars ?? {};
|
|
1208
|
+
const exportSection = parsed.export ?? {};
|
|
1209
|
+
const resolved: ThemeExportPalette = {
|
|
1210
|
+
pageBg: resolveThemeJsonValue(exportSection.pageBg, vars),
|
|
1211
|
+
cardBg: resolveThemeJsonValue(exportSection.cardBg, vars),
|
|
1212
|
+
infoBg: resolveThemeJsonValue(exportSection.infoBg, vars),
|
|
1213
|
+
};
|
|
1214
|
+
return resolved.pageBg || resolved.cardBg || resolved.infoBg ? resolved : undefined;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
function readThemeColorToken(theme: Theme | undefined, token: string): string | undefined {
|
|
1218
|
+
const parsed = readThemeSourceJson(theme);
|
|
1219
|
+
if (!parsed) return undefined;
|
|
1220
|
+
return resolveThemeJsonValue(parsed.colors?.[token], parsed.vars ?? {});
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
function readThemeVarColor(theme: Theme | undefined, keys: string[]): string | undefined {
|
|
1224
|
+
const parsed = readThemeSourceJson(theme);
|
|
1225
|
+
if (!parsed) return undefined;
|
|
1226
|
+
const vars = parsed.vars ?? {};
|
|
1227
|
+
for (const key of keys) {
|
|
1228
|
+
const color = resolveThemeJsonValue(vars[key], vars);
|
|
1229
|
+
if (color) return color;
|
|
1230
|
+
}
|
|
1231
|
+
return undefined;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
function readThemeAnyColor(theme: Theme | undefined, keys: string[]): string | undefined {
|
|
1235
|
+
const parsed = readThemeSourceJson(theme);
|
|
1236
|
+
if (!parsed) return undefined;
|
|
1237
|
+
const vars = parsed.vars ?? {};
|
|
1238
|
+
for (const key of keys) {
|
|
1239
|
+
const color = resolveThemeJsonValue(parsed.colors?.[key], vars);
|
|
1240
|
+
if (color) return color;
|
|
1241
|
+
}
|
|
1242
|
+
return undefined;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
function inferThemeModeFromColor(color: string | undefined): StudioThemeMode | undefined {
|
|
1246
|
+
if (!color || !hexToRgb(color)) return undefined;
|
|
1247
|
+
return relativeLuminance(color) >= 0.58 ? "light" : "dark";
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
function inferThemeTextColor(theme: Theme | undefined, mode: StudioThemeMode): string | undefined {
|
|
1251
|
+
return readThemeAnyColor(theme, ["text", "userMessageText", "customMessageText", "mdCodeBlock"])
|
|
1252
|
+
?? readThemeVarColor(
|
|
1253
|
+
theme,
|
|
1254
|
+
mode === "light"
|
|
1255
|
+
? ["text", "fg", "foreground", "textDark1", "fg0", "fg1", "nord0"]
|
|
1256
|
+
: ["text", "fg", "foreground", "text", "fg0", "fg1", "subtext1", "subtext0", "nord4", "gray3"],
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
function inferThemeSurfaceColor(theme: Theme | undefined, role: "page" | "card" | "panel2"): string | undefined {
|
|
1261
|
+
if (role === "page") {
|
|
1262
|
+
return readThemeVarColor(theme, ["pageBg", "bg", "base", "background", "mantle", "bg_dark", "bg0", "nord0"]);
|
|
1263
|
+
}
|
|
1264
|
+
if (role === "card") {
|
|
1265
|
+
return readThemeVarColor(theme, ["cardBg", "surface", "base", "bg", "bg1", "nord1"]);
|
|
1266
|
+
}
|
|
1267
|
+
return readThemeVarColor(theme, ["infoBg", "surfaceAlt", "surface0", "overlay", "bg_hl", "bg2", "nord2"]);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1105
1270
|
function getStudioThemeStyle(theme?: Theme): StudioThemeStyle {
|
|
1106
1271
|
const mode = getStudioThemeMode(theme);
|
|
1107
1272
|
const fallback = mode === "light" ? LIGHT_STUDIO_PALETTE : DARK_STUDIO_PALETTE;
|
|
@@ -1116,36 +1281,49 @@ function getStudioThemeStyle(theme?: Theme): StudioThemeStyle {
|
|
|
1116
1281
|
const accent =
|
|
1117
1282
|
safeThemeColor(() => theme.getFgAnsi("mdLink"))
|
|
1118
1283
|
?? safeThemeColor(() => theme.getFgAnsi("accent"))
|
|
1284
|
+
?? readThemeColorToken(theme, "mdLink")
|
|
1285
|
+
?? readThemeColorToken(theme, "accent")
|
|
1119
1286
|
?? fallback.accent;
|
|
1120
|
-
const warn = safeThemeColor(() => theme.getFgAnsi("warning")) ?? fallback.warn;
|
|
1121
|
-
const error = safeThemeColor(() => theme.getFgAnsi("error")) ?? fallback.error;
|
|
1122
|
-
const ok = safeThemeColor(() => theme.getFgAnsi("success")) ?? fallback.ok;
|
|
1287
|
+
const warn = safeThemeColor(() => theme.getFgAnsi("warning")) ?? readThemeColorToken(theme, "warning") ?? fallback.warn;
|
|
1288
|
+
const error = safeThemeColor(() => theme.getFgAnsi("error")) ?? readThemeColorToken(theme, "error") ?? fallback.error;
|
|
1289
|
+
const ok = safeThemeColor(() => theme.getFgAnsi("success")) ?? readThemeColorToken(theme, "success") ?? fallback.ok;
|
|
1290
|
+
const text = safeThemeColor(() => theme.getFgAnsi("text")) ?? inferThemeTextColor(theme, mode) ?? fallback.text;
|
|
1123
1291
|
const exported = readThemeExportPalette(theme);
|
|
1124
1292
|
|
|
1125
1293
|
const surfaceBase =
|
|
1126
1294
|
safeThemeColor(() => theme.getBgAnsi("userMessageBg"))
|
|
1127
|
-
?? safeThemeColor(() => theme.getBgAnsi("customMessageBg"))
|
|
1295
|
+
?? safeThemeColor(() => theme.getBgAnsi("customMessageBg"))
|
|
1296
|
+
?? readThemeColorToken(theme, "userMessageBg")
|
|
1297
|
+
?? readThemeColorToken(theme, "customMessageBg");
|
|
1128
1298
|
const derived = surfaceBase ? deriveCanvasColors(surfaceBase, mode) : undefined;
|
|
1299
|
+
const themePageBg = inferThemeSurfaceColor(theme, "page");
|
|
1300
|
+
const themeCardBg = inferThemeSurfaceColor(theme, "card");
|
|
1301
|
+
const themePanel2 = inferThemeSurfaceColor(theme, "panel2");
|
|
1129
1302
|
|
|
1130
1303
|
const palette: StudioPalette = {
|
|
1131
1304
|
bg:
|
|
1132
1305
|
exported?.pageBg
|
|
1306
|
+
?? themePageBg
|
|
1133
1307
|
?? derived?.pageBg
|
|
1134
1308
|
?? fallback.bg,
|
|
1135
1309
|
panel:
|
|
1136
1310
|
exported?.cardBg
|
|
1311
|
+
?? themeCardBg
|
|
1137
1312
|
?? derived?.cardBg
|
|
1138
1313
|
?? safeThemeColor(() => theme.getBgAnsi("toolPendingBg"))
|
|
1314
|
+
?? readThemeColorToken(theme, "toolPendingBg")
|
|
1139
1315
|
?? fallback.panel,
|
|
1140
1316
|
panel2:
|
|
1141
|
-
|
|
1317
|
+
themePanel2
|
|
1318
|
+
?? derived?.panel2
|
|
1142
1319
|
?? safeThemeColor(() => theme.getBgAnsi("selectedBg"))
|
|
1320
|
+
?? readThemeColorToken(theme, "selectedBg")
|
|
1143
1321
|
?? exported?.infoBg
|
|
1144
1322
|
?? fallback.panel2,
|
|
1145
|
-
border: safeThemeColor(() => theme.getFgAnsi("border")) ?? fallback.border,
|
|
1146
|
-
borderMuted: safeThemeColor(() => theme.getFgAnsi("borderMuted")) ?? fallback.borderMuted,
|
|
1147
|
-
text
|
|
1148
|
-
muted: safeThemeColor(() => theme.getFgAnsi("muted")) ?? fallback.muted,
|
|
1323
|
+
border: safeThemeColor(() => theme.getFgAnsi("border")) ?? readThemeColorToken(theme, "border") ?? fallback.border,
|
|
1324
|
+
borderMuted: safeThemeColor(() => theme.getFgAnsi("borderMuted")) ?? readThemeColorToken(theme, "borderMuted") ?? fallback.borderMuted,
|
|
1325
|
+
text,
|
|
1326
|
+
muted: safeThemeColor(() => theme.getFgAnsi("muted")) ?? readThemeColorToken(theme, "muted") ?? fallback.muted,
|
|
1149
1327
|
accent,
|
|
1150
1328
|
warn,
|
|
1151
1329
|
error,
|
|
@@ -1156,28 +1334,33 @@ function getStudioThemeStyle(theme?: Theme): StudioThemeStyle {
|
|
|
1156
1334
|
accentSoftStrong: withAlpha(accent, mode === "light" ? 0.35 : 0.40, fallback.accentSoftStrong),
|
|
1157
1335
|
okBorder: withAlpha(ok, mode === "light" ? 0.55 : 0.70, fallback.okBorder),
|
|
1158
1336
|
warnBorder: withAlpha(warn, mode === "light" ? 0.55 : 0.70, fallback.warnBorder),
|
|
1159
|
-
mdHeading: safeThemeColor(() => theme.getFgAnsi("mdHeading")) ?? fallback.mdHeading,
|
|
1160
|
-
mdLink: safeThemeColor(() => theme.getFgAnsi("mdLink")) ?? fallback.mdLink,
|
|
1161
|
-
mdLinkUrl: safeThemeColor(() => theme.getFgAnsi("mdLinkUrl")) ?? fallback.mdLinkUrl,
|
|
1162
|
-
mdCode: safeThemeColor(() => theme.getFgAnsi("mdCode")) ?? fallback.mdCode,
|
|
1163
|
-
mdCodeBlock: safeThemeColor(() => theme.getFgAnsi("mdCodeBlock")) ??
|
|
1164
|
-
mdCodeBlockBorder: safeThemeColor(() => theme.getFgAnsi("mdCodeBlockBorder")) ?? fallback.mdCodeBlockBorder,
|
|
1165
|
-
mdQuote: safeThemeColor(() => theme.getFgAnsi("mdQuote")) ?? fallback.mdQuote,
|
|
1166
|
-
mdQuoteBorder: safeThemeColor(() => theme.getFgAnsi("mdQuoteBorder")) ?? fallback.mdQuoteBorder,
|
|
1167
|
-
mdHr: safeThemeColor(() => theme.getFgAnsi("mdHr")) ?? fallback.mdHr,
|
|
1168
|
-
mdListBullet: safeThemeColor(() => theme.getFgAnsi("mdListBullet")) ?? fallback.mdListBullet,
|
|
1169
|
-
syntaxComment: safeThemeColor(() => theme.getFgAnsi("syntaxComment")) ?? fallback.syntaxComment,
|
|
1170
|
-
syntaxKeyword: safeThemeColor(() => theme.getFgAnsi("syntaxKeyword")) ?? fallback.syntaxKeyword,
|
|
1171
|
-
syntaxFunction: safeThemeColor(() => theme.getFgAnsi("syntaxFunction")) ?? fallback.syntaxFunction,
|
|
1172
|
-
syntaxVariable: safeThemeColor(() => theme.getFgAnsi("syntaxVariable")) ?? fallback.syntaxVariable,
|
|
1173
|
-
syntaxString: safeThemeColor(() => theme.getFgAnsi("syntaxString")) ?? fallback.syntaxString,
|
|
1174
|
-
syntaxNumber: safeThemeColor(() => theme.getFgAnsi("syntaxNumber")) ?? fallback.syntaxNumber,
|
|
1175
|
-
syntaxType: safeThemeColor(() => theme.getFgAnsi("syntaxType")) ?? fallback.syntaxType,
|
|
1176
|
-
syntaxOperator: safeThemeColor(() => theme.getFgAnsi("syntaxOperator")) ?? fallback.syntaxOperator,
|
|
1177
|
-
syntaxPunctuation: safeThemeColor(() => theme.getFgAnsi("syntaxPunctuation")) ?? fallback.syntaxPunctuation,
|
|
1337
|
+
mdHeading: safeThemeColor(() => theme.getFgAnsi("mdHeading")) ?? readThemeColorToken(theme, "mdHeading") ?? fallback.mdHeading,
|
|
1338
|
+
mdLink: safeThemeColor(() => theme.getFgAnsi("mdLink")) ?? readThemeColorToken(theme, "mdLink") ?? fallback.mdLink,
|
|
1339
|
+
mdLinkUrl: safeThemeColor(() => theme.getFgAnsi("mdLinkUrl")) ?? readThemeColorToken(theme, "mdLinkUrl") ?? fallback.mdLinkUrl,
|
|
1340
|
+
mdCode: safeThemeColor(() => theme.getFgAnsi("mdCode")) ?? readThemeColorToken(theme, "mdCode") ?? fallback.mdCode,
|
|
1341
|
+
mdCodeBlock: safeThemeColor(() => theme.getFgAnsi("mdCodeBlock")) ?? readThemeColorToken(theme, "mdCodeBlock") ?? text,
|
|
1342
|
+
mdCodeBlockBorder: safeThemeColor(() => theme.getFgAnsi("mdCodeBlockBorder")) ?? readThemeColorToken(theme, "mdCodeBlockBorder") ?? fallback.mdCodeBlockBorder,
|
|
1343
|
+
mdQuote: safeThemeColor(() => theme.getFgAnsi("mdQuote")) ?? readThemeColorToken(theme, "mdQuote") ?? fallback.mdQuote,
|
|
1344
|
+
mdQuoteBorder: safeThemeColor(() => theme.getFgAnsi("mdQuoteBorder")) ?? readThemeColorToken(theme, "mdQuoteBorder") ?? fallback.mdQuoteBorder,
|
|
1345
|
+
mdHr: safeThemeColor(() => theme.getFgAnsi("mdHr")) ?? readThemeColorToken(theme, "mdHr") ?? fallback.mdHr,
|
|
1346
|
+
mdListBullet: safeThemeColor(() => theme.getFgAnsi("mdListBullet")) ?? readThemeColorToken(theme, "mdListBullet") ?? fallback.mdListBullet,
|
|
1347
|
+
syntaxComment: safeThemeColor(() => theme.getFgAnsi("syntaxComment")) ?? readThemeColorToken(theme, "syntaxComment") ?? fallback.syntaxComment,
|
|
1348
|
+
syntaxKeyword: safeThemeColor(() => theme.getFgAnsi("syntaxKeyword")) ?? readThemeColorToken(theme, "syntaxKeyword") ?? fallback.syntaxKeyword,
|
|
1349
|
+
syntaxFunction: safeThemeColor(() => theme.getFgAnsi("syntaxFunction")) ?? readThemeColorToken(theme, "syntaxFunction") ?? fallback.syntaxFunction,
|
|
1350
|
+
syntaxVariable: safeThemeColor(() => theme.getFgAnsi("syntaxVariable")) ?? readThemeColorToken(theme, "syntaxVariable") ?? fallback.syntaxVariable,
|
|
1351
|
+
syntaxString: safeThemeColor(() => theme.getFgAnsi("syntaxString")) ?? readThemeColorToken(theme, "syntaxString") ?? fallback.syntaxString,
|
|
1352
|
+
syntaxNumber: safeThemeColor(() => theme.getFgAnsi("syntaxNumber")) ?? readThemeColorToken(theme, "syntaxNumber") ?? fallback.syntaxNumber,
|
|
1353
|
+
syntaxType: safeThemeColor(() => theme.getFgAnsi("syntaxType")) ?? readThemeColorToken(theme, "syntaxType") ?? fallback.syntaxType,
|
|
1354
|
+
syntaxOperator: safeThemeColor(() => theme.getFgAnsi("syntaxOperator")) ?? readThemeColorToken(theme, "syntaxOperator") ?? fallback.syntaxOperator,
|
|
1355
|
+
syntaxPunctuation: safeThemeColor(() => theme.getFgAnsi("syntaxPunctuation")) ?? readThemeColorToken(theme, "syntaxPunctuation") ?? fallback.syntaxPunctuation,
|
|
1178
1356
|
};
|
|
1179
1357
|
|
|
1180
|
-
return {
|
|
1358
|
+
return {
|
|
1359
|
+
mode,
|
|
1360
|
+
palette,
|
|
1361
|
+
accentContrast: readThemeVarColor(theme, ["studioAccentText", "studioAccentContrast"]),
|
|
1362
|
+
errorContrast: readThemeVarColor(theme, ["studioErrorText", "studioErrorContrast"]),
|
|
1363
|
+
};
|
|
1181
1364
|
}
|
|
1182
1365
|
|
|
1183
1366
|
function createSessionToken(): string {
|
|
@@ -5966,6 +6149,17 @@ function buildTerminalSessionLabel(cwd: string, sessionName?: string): string {
|
|
|
5966
6149
|
return parts.join(" · ");
|
|
5967
6150
|
}
|
|
5968
6151
|
|
|
6152
|
+
function buildTerminalSessionDetail(cwd: string, sessionName?: string): string {
|
|
6153
|
+
const termProgram = String(process.env.TERM_PROGRAM ?? "").trim() || "unknown";
|
|
6154
|
+
const name = String(sessionName ?? "").trim() || "unknown";
|
|
6155
|
+
const workingDir = String(cwd || process.cwd() || "").trim() || "unknown";
|
|
6156
|
+
return [
|
|
6157
|
+
`Terminal: ${termProgram}`,
|
|
6158
|
+
`Session: ${name}`,
|
|
6159
|
+
`Working dir: ${workingDir}`,
|
|
6160
|
+
].join("\n");
|
|
6161
|
+
}
|
|
6162
|
+
|
|
5969
6163
|
function sanitizePdfFilename(input: string | undefined): string {
|
|
5970
6164
|
const fallback = "studio-preview.pdf";
|
|
5971
6165
|
const raw = String(input ?? "").trim();
|
|
@@ -5984,12 +6178,23 @@ function sanitizePdfFilename(input: string | undefined): string {
|
|
|
5984
6178
|
}
|
|
5985
6179
|
|
|
5986
6180
|
function buildThemeCssVars(style: StudioThemeStyle): Record<string, string> {
|
|
6181
|
+
const shadowColor = style.mode === "light"
|
|
6182
|
+
? withAlpha(style.palette.text, 0.10, "rgba(15, 23, 42, 0.08)")
|
|
6183
|
+
: "rgba(0, 0, 0, 0.32)";
|
|
5987
6184
|
const panelShadow =
|
|
5988
6185
|
style.mode === "light"
|
|
5989
|
-
?
|
|
5990
|
-
: "0 1px 2px rgba(0, 0, 0, 0.
|
|
5991
|
-
const
|
|
5992
|
-
const
|
|
6186
|
+
? `0 1px 2px ${withAlpha(style.palette.text, 0.035, "rgba(15, 23, 42, 0.03)")}, 0 4px 14px ${withAlpha(style.palette.text, 0.055, "rgba(15, 23, 42, 0.04)")}`
|
|
6187
|
+
: "0 1px 2px rgba(0, 0, 0, 0.30), 0 6px 18px rgba(0, 0, 0, 0.18)";
|
|
6188
|
+
const rawBorderSubtle = blendColors(style.palette.borderMuted, style.palette.panel, style.mode === "light" ? 0.58 : 0.48);
|
|
6189
|
+
const rawPanelBorder = blendColors(style.palette.borderMuted, style.palette.panel, style.mode === "light" ? 0.42 : 0.36);
|
|
6190
|
+
const rawControlBorder = blendColors(style.palette.borderMuted, style.palette.panel, style.mode === "light" ? 0.30 : 0.22);
|
|
6191
|
+
const rawPaneActiveBorder = blendColors(style.palette.border, style.palette.panel, style.mode === "light" ? 0.34 : 0.48);
|
|
6192
|
+
const borderSubtle = capBorderContrast(rawBorderSubtle, style.palette.panel, style.mode === "light" ? 1.10 : 1.12);
|
|
6193
|
+
const panelBorder = capBorderContrast(rawPanelBorder, style.palette.panel, style.mode === "light" ? 1.15 : 1.18);
|
|
6194
|
+
const controlBorder = capBorderContrast(rawControlBorder, style.palette.panel, style.mode === "light" ? 1.22 : 1.25);
|
|
6195
|
+
const paneActiveBorder = capBorderContrast(rawPaneActiveBorder, style.palette.panel, style.mode === "light" ? 1.38 : 1.45);
|
|
6196
|
+
const accentContrast = style.accentContrast ?? (style.mode === "light" ? "#ffffff" : "#0e1616");
|
|
6197
|
+
const errorContrast = style.errorContrast ?? readableTextOn(style.palette.error);
|
|
5993
6198
|
const blockquoteBg = withAlpha(
|
|
5994
6199
|
style.palette.mdQuoteBorder,
|
|
5995
6200
|
style.mode === "light" ? 0.10 : 0.16,
|
|
@@ -6000,10 +6205,36 @@ function buildThemeCssVars(style: StudioThemeStyle): Record<string, string> {
|
|
|
6000
6205
|
style.mode === "light" ? 0.10 : 0.14,
|
|
6001
6206
|
style.mode === "light" ? "rgba(15, 23, 42, 0.03)" : "rgba(255, 255, 255, 0.04)",
|
|
6002
6207
|
);
|
|
6003
|
-
const
|
|
6004
|
-
|
|
6208
|
+
const inlineCodeBg = withAlpha(
|
|
6209
|
+
style.palette.mdCodeBlockBorder,
|
|
6210
|
+
style.mode === "light" ? 0.13 : 0.18,
|
|
6211
|
+
style.mode === "light" ? "rgba(15, 23, 42, 0.06)" : "rgba(255, 255, 255, 0.07)",
|
|
6212
|
+
);
|
|
6213
|
+
const diffAddedBg = withAlpha(style.palette.ok, style.mode === "light" ? 0.10 : 0.14, "rgba(46, 160, 67, 0.12)");
|
|
6214
|
+
const diffRemovedBg = withAlpha(style.palette.error, style.mode === "light" ? 0.10 : 0.14, "rgba(248, 81, 73, 0.12)");
|
|
6215
|
+
const okSoft = withAlpha(style.palette.ok, style.mode === "light" ? 0.10 : 0.12, "rgba(115, 209, 61, 0.08)");
|
|
6216
|
+
const errorSoft = withAlpha(style.palette.error, style.mode === "light" ? 0.10 : 0.12, "rgba(255, 107, 107, 0.08)");
|
|
6217
|
+
const backdropBg = style.mode === "light" ? "rgba(15, 23, 42, 0.20)" : "rgba(0, 0, 0, 0.48)";
|
|
6218
|
+
const panelLum = hexToRgb(style.palette.panel) ? relativeLuminance(style.palette.panel) : null;
|
|
6219
|
+
const panel2Lum = hexToRgb(style.palette.panel2) ? relativeLuminance(style.palette.panel2) : null;
|
|
6220
|
+
const lightPrimarySurface = panelLum != null && panel2Lum != null && panel2Lum > panelLum
|
|
6221
|
+
? style.palette.panel2
|
|
6005
6222
|
: style.palette.panel;
|
|
6223
|
+
const lightSecondarySurface = lightPrimarySurface === style.palette.panel ? style.palette.panel2 : style.palette.panel;
|
|
6224
|
+
const editorBg = style.mode === "light" ? lightPrimarySurface : style.palette.panel;
|
|
6225
|
+
const editorGutterBg = style.mode === "light" ? lightSecondarySurface : style.palette.panel2;
|
|
6226
|
+
const referenceMetaBg = style.mode === "light" ? lightSecondarySurface : style.palette.panel2;
|
|
6227
|
+
const referenceBadgeBg = style.mode === "light" ? lightPrimarySurface : style.palette.panel;
|
|
6228
|
+
const scratchpadHeaderBg = style.mode === "light" ? lightSecondarySurface : style.palette.panel2;
|
|
6229
|
+
const scratchpadBodyBg = style.mode === "light" ? lightPrimarySurface : style.palette.panel;
|
|
6230
|
+
const infoText = blendColors(style.palette.text, style.palette.muted, style.mode === "light" ? 0.36 : 0.30);
|
|
6231
|
+
const headerActionBg = style.mode === "light" ? lightPrimarySurface : "transparent";
|
|
6232
|
+
const headerActionHoverBg = style.mode === "light" ? lightPrimarySurface : style.palette.panel2;
|
|
6233
|
+
const headerActionBorder = style.mode === "light" ? controlBorder : "transparent";
|
|
6234
|
+
const headerFilledBg = style.mode === "light" ? lightPrimarySurface : style.palette.panel2;
|
|
6006
6235
|
const monoFontStack = getStudioMonoFontStack();
|
|
6236
|
+
const uiFontStack = getStudioUiFontStack();
|
|
6237
|
+
const proseFontStack = getStudioProseFontStack();
|
|
6007
6238
|
|
|
6008
6239
|
return {
|
|
6009
6240
|
"color-scheme": style.mode,
|
|
@@ -6012,6 +6243,10 @@ function buildThemeCssVars(style: StudioThemeStyle): Record<string, string> {
|
|
|
6012
6243
|
"--panel-2": style.palette.panel2,
|
|
6013
6244
|
"--border": style.palette.border,
|
|
6014
6245
|
"--border-muted": style.palette.borderMuted,
|
|
6246
|
+
"--border-subtle": borderSubtle,
|
|
6247
|
+
"--panel-border": panelBorder,
|
|
6248
|
+
"--control-border": controlBorder,
|
|
6249
|
+
"--pane-active-border": paneActiveBorder,
|
|
6015
6250
|
"--text": style.palette.text,
|
|
6016
6251
|
"--muted": style.palette.muted,
|
|
6017
6252
|
"--accent": style.palette.accent,
|
|
@@ -6044,11 +6279,31 @@ function buildThemeCssVars(style: StudioThemeStyle): Record<string, string> {
|
|
|
6044
6279
|
"--syntax-operator": style.palette.syntaxOperator,
|
|
6045
6280
|
"--syntax-punctuation": style.palette.syntaxPunctuation,
|
|
6046
6281
|
"--panel-shadow": panelShadow,
|
|
6282
|
+
"--shadow-color": shadowColor,
|
|
6047
6283
|
"--accent-contrast": accentContrast,
|
|
6048
6284
|
"--error-contrast": errorContrast,
|
|
6049
6285
|
"--blockquote-bg": blockquoteBg,
|
|
6286
|
+
"--inline-code-bg": inlineCodeBg,
|
|
6050
6287
|
"--table-alt-bg": tableAltBg,
|
|
6288
|
+
"--md-table-border": borderSubtle,
|
|
6289
|
+
"--diff-added-bg": diffAddedBg,
|
|
6290
|
+
"--diff-removed-bg": diffRemovedBg,
|
|
6291
|
+
"--ok-soft": okSoft,
|
|
6292
|
+
"--error-soft": errorSoft,
|
|
6293
|
+
"--backdrop-bg": backdropBg,
|
|
6051
6294
|
"--editor-bg": editorBg,
|
|
6295
|
+
"--editor-gutter-bg": editorGutterBg,
|
|
6296
|
+
"--reference-meta-bg": referenceMetaBg,
|
|
6297
|
+
"--reference-badge-bg": referenceBadgeBg,
|
|
6298
|
+
"--scratchpad-header-bg": scratchpadHeaderBg,
|
|
6299
|
+
"--scratchpad-body-bg": scratchpadBodyBg,
|
|
6300
|
+
"--studio-info-text": infoText,
|
|
6301
|
+
"--studio-header-action-bg": headerActionBg,
|
|
6302
|
+
"--studio-header-action-hover-bg": headerActionHoverBg,
|
|
6303
|
+
"--studio-header-action-border": headerActionBorder,
|
|
6304
|
+
"--studio-header-filled-bg": headerFilledBg,
|
|
6305
|
+
"--font-ui": uiFontStack,
|
|
6306
|
+
"--font-prose": proseFontStack,
|
|
6052
6307
|
"--font-mono": monoFontStack,
|
|
6053
6308
|
};
|
|
6054
6309
|
}
|
|
@@ -6069,6 +6324,7 @@ function buildStudioHtml(
|
|
|
6069
6324
|
theme?: Theme,
|
|
6070
6325
|
initialModelLabel?: string,
|
|
6071
6326
|
initialTerminalLabel?: string,
|
|
6327
|
+
initialTerminalDetail?: string,
|
|
6072
6328
|
initialContextUsage?: StudioContextUsageSnapshot,
|
|
6073
6329
|
studioMode: StudioUiMode = "full",
|
|
6074
6330
|
): string {
|
|
@@ -6079,6 +6335,7 @@ function buildStudioHtml(
|
|
|
6079
6335
|
const initialDraftId = escapeHtmlForInline(initialDocument?.draftId ?? "");
|
|
6080
6336
|
const initialModel = escapeHtmlForInline(initialModelLabel ?? "none");
|
|
6081
6337
|
const initialTerminal = escapeHtmlForInline(initialTerminalLabel ?? "unknown");
|
|
6338
|
+
const initialTerminalDetailAttr = escapeHtmlForInline(initialTerminalDetail ?? initialTerminalLabel ?? "unknown");
|
|
6082
6339
|
const initialContextTokens =
|
|
6083
6340
|
typeof initialContextUsage?.tokens === "number" && Number.isFinite(initialContextUsage.tokens)
|
|
6084
6341
|
? String(initialContextUsage.tokens)
|
|
@@ -6145,7 +6402,7 @@ ${cssVarsBlock}
|
|
|
6145
6402
|
</style>
|
|
6146
6403
|
<link rel="stylesheet" href="${stylesheetHref}" />
|
|
6147
6404
|
</head>
|
|
6148
|
-
<body data-initial-source="${initialSource}" data-initial-label="${initialLabel}" data-initial-path="${initialPath}" data-initial-draft-id="${initialDraftId}" data-model-label="${initialModel}" data-terminal-label="${initialTerminal}" data-context-tokens="${initialContextTokens}" data-context-window="${initialContextWindow}" data-context-percent="${initialContextPercent}" data-studio-mode="${studioMode}">
|
|
6405
|
+
<body data-initial-source="${initialSource}" data-initial-label="${initialLabel}" data-initial-path="${initialPath}" data-initial-draft-id="${initialDraftId}" data-model-label="${initialModel}" data-terminal-label="${initialTerminal}" data-terminal-detail="${initialTerminalDetailAttr}" data-context-tokens="${initialContextTokens}" data-context-window="${initialContextWindow}" data-context-percent="${initialContextPercent}" data-studio-mode="${studioMode}">
|
|
6149
6406
|
<header>
|
|
6150
6407
|
<h1><span class="app-logo" aria-hidden="true">π</span> Studio <span class="app-subtitle">${appSubtitle}</span></h1>
|
|
6151
6408
|
<div class="controls">
|
|
@@ -6241,6 +6498,16 @@ ${cssVarsBlock}
|
|
|
6241
6498
|
<option value="off">Line numbers: Off</option>
|
|
6242
6499
|
<option value="on" selected>Line numbers: On</option>
|
|
6243
6500
|
</select>
|
|
6501
|
+
<select id="editorFontSizeSelect" aria-label="Editor text size" title="Adjust raw editor text size.">
|
|
6502
|
+
<option value="10">Editor text: 10px</option>
|
|
6503
|
+
<option value="11">Editor text: 11px</option>
|
|
6504
|
+
<option value="12" selected>Editor text: 12px</option>
|
|
6505
|
+
<option value="13">Editor text: 13px</option>
|
|
6506
|
+
<option value="14">Editor text: 14px</option>
|
|
6507
|
+
<option value="15">Editor text: 15px</option>
|
|
6508
|
+
<option value="16">Editor text: 16px</option>
|
|
6509
|
+
<option value="18">Editor text: 18px</option>
|
|
6510
|
+
</select>
|
|
6244
6511
|
</div>
|
|
6245
6512
|
</div>
|
|
6246
6513
|
</div>
|
|
@@ -6334,7 +6601,7 @@ ${cssVarsBlock}
|
|
|
6334
6601
|
<div id="critiqueView" class="panel-scroll rendered-markdown"><pre class="plain-markdown">No response yet.</pre></div>
|
|
6335
6602
|
<div class="response-wrap">
|
|
6336
6603
|
<div id="responseActions" class="response-actions">
|
|
6337
|
-
<div class="response-actions-row">
|
|
6604
|
+
<div class="response-actions-row response-options-row">
|
|
6338
6605
|
<select id="followSelect" aria-label="Auto-update response">
|
|
6339
6606
|
<option value="on" selected>Auto-update response: On</option>
|
|
6340
6607
|
<option value="off">Auto-update response: Off</option>
|
|
@@ -6343,6 +6610,20 @@ ${cssVarsBlock}
|
|
|
6343
6610
|
<option value="off">Syntax highlight: Off</option>
|
|
6344
6611
|
<option value="on" selected>Syntax highlight: On</option>
|
|
6345
6612
|
</select>
|
|
6613
|
+
<select id="responseFontSizeSelect" aria-label="Response text size" title="Adjust right-pane response, preview, and working text size.">
|
|
6614
|
+
<option value="11">Response text: 11px</option>
|
|
6615
|
+
<option value="12">Response text: 12px</option>
|
|
6616
|
+
<option value="12.5">Response text: 12.5px</option>
|
|
6617
|
+
<option value="13">Response text: 13px</option>
|
|
6618
|
+
<option value="13.5" selected>Response text: 13.5px</option>
|
|
6619
|
+
<option value="14">Response text: 14px</option>
|
|
6620
|
+
<option value="14.5">Response text: 14.5px</option>
|
|
6621
|
+
<option value="15">Response text: 15px</option>
|
|
6622
|
+
<option value="15.5">Response text: 15.5px</option>
|
|
6623
|
+
<option value="16">Response text: 16px</option>
|
|
6624
|
+
<option value="18">Response text: 18px</option>
|
|
6625
|
+
<option value="20">Response text: 20px</option>
|
|
6626
|
+
</select>
|
|
6346
6627
|
</div>
|
|
6347
6628
|
<div class="response-actions-row history-row">
|
|
6348
6629
|
<button id="pullLatestBtn" type="button" title="Fetch the latest assistant response when auto-update is off.">Fetch latest response</button>
|
|
@@ -6351,7 +6632,7 @@ ${cssVarsBlock}
|
|
|
6351
6632
|
<button id="historyNextBtn" type="button" title="Show next response in history.">Next response ▶</button>
|
|
6352
6633
|
<button id="historyLastBtn" type="button" title="Jump to the latest loaded response in history.">Last response ▶|</button>
|
|
6353
6634
|
</div>
|
|
6354
|
-
<div class="response-actions-row">
|
|
6635
|
+
<div class="response-actions-row response-result-row">
|
|
6355
6636
|
<button id="loadResponseBtn" type="button">Load response into editor</button>
|
|
6356
6637
|
<button id="loadCritiqueNotesBtn" type="button" hidden>Load critique notes into editor</button>
|
|
6357
6638
|
<button id="loadCritiqueFullBtn" type="button" hidden>Load full critique into editor</button>
|
|
@@ -6365,7 +6646,7 @@ ${cssVarsBlock}
|
|
|
6365
6646
|
|
|
6366
6647
|
<footer>
|
|
6367
6648
|
<span id="statusLine"><span id="statusSpinner" aria-hidden="true"> </span><span id="status">Booting studio…</span></span>
|
|
6368
|
-
<span id="footerMeta" class="footer-meta"><span id="footerMetaText" class="footer-meta-text"
|
|
6649
|
+
<span id="footerMeta" class="footer-meta"><span id="footerMetaText" class="footer-meta-text"><span id="footerMetaModel" class="footer-meta-part footer-meta-model">${initialModel}</span><span class="footer-meta-sep">·</span><span id="footerMetaTerminal" class="footer-meta-part footer-meta-terminal">${initialTerminal}</span><span class="footer-meta-sep">·</span><span id="footerMetaContext" class="footer-meta-part footer-meta-context">unknown</span></span><button id="compactBtn" class="footer-compact-btn" type="button" title="Trigger pi context compaction now.">Compact</button></span>
|
|
6369
6650
|
<span class="shortcut-hint">Focus pane: F10 (or Cmd/Ctrl+Esc) to toggle · Save editor: Cmd/Ctrl+S · Run / queue steering: Cmd/Ctrl+Enter · Stop request: Esc</span>
|
|
6370
6651
|
</footer>
|
|
6371
6652
|
|
|
@@ -6424,6 +6705,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6424
6705
|
let currentModel: { provider?: string; id?: string } | undefined;
|
|
6425
6706
|
let currentModelLabel = "none";
|
|
6426
6707
|
let terminalSessionLabel = buildTerminalSessionLabel(studioCwd);
|
|
6708
|
+
let terminalSessionDetail = buildTerminalSessionDetail(studioCwd);
|
|
6427
6709
|
let studioResponseHistory: StudioResponseHistoryItem[] = [];
|
|
6428
6710
|
let latestSessionUserPrompt: string | null = null;
|
|
6429
6711
|
let pendingTurnPrompt: string | null = null;
|
|
@@ -6508,6 +6790,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6508
6790
|
const baseModelLabel = formatModelLabel(currentModel);
|
|
6509
6791
|
currentModelLabel = formatModelLabelWithThinking(baseModelLabel, getThinkingLevelSafe());
|
|
6510
6792
|
terminalSessionLabel = buildTerminalSessionLabel(studioCwd, getSessionNameSafe());
|
|
6793
|
+
terminalSessionDetail = buildTerminalSessionDetail(studioCwd, getSessionNameSafe());
|
|
6511
6794
|
};
|
|
6512
6795
|
|
|
6513
6796
|
const notifyStudio = (message: string, level: "info" | "warning" | "error" = "info") => {
|
|
@@ -7081,6 +7364,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
7081
7364
|
|
|
7082
7365
|
const broadcastState = () => {
|
|
7083
7366
|
terminalSessionLabel = buildTerminalSessionLabel(studioCwd, getSessionNameSafe());
|
|
7367
|
+
terminalSessionDetail = buildTerminalSessionDetail(studioCwd, getSessionNameSafe());
|
|
7084
7368
|
currentModelLabel = formatModelLabelWithThinking(formatModelLabel(currentModel), getThinkingLevelSafe());
|
|
7085
7369
|
refreshContextUsage();
|
|
7086
7370
|
broadcast({
|
|
@@ -7092,6 +7376,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
7092
7376
|
terminalActivityLabel,
|
|
7093
7377
|
modelLabel: currentModelLabel,
|
|
7094
7378
|
terminalSessionLabel,
|
|
7379
|
+
terminalSessionDetail,
|
|
7095
7380
|
contextTokens: contextUsageSnapshot.tokens,
|
|
7096
7381
|
contextWindow: contextUsageSnapshot.contextWindow,
|
|
7097
7382
|
contextPercent: contextUsageSnapshot.percent,
|
|
@@ -7386,6 +7671,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
7386
7671
|
terminalActivityLabel,
|
|
7387
7672
|
modelLabel: currentModelLabel,
|
|
7388
7673
|
terminalSessionLabel,
|
|
7674
|
+
terminalSessionDetail,
|
|
7389
7675
|
contextTokens: contextUsageSnapshot.tokens,
|
|
7390
7676
|
contextWindow: contextUsageSnapshot.contextWindow,
|
|
7391
7677
|
contextPercent: contextUsageSnapshot.percent,
|
|
@@ -8511,7 +8797,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
8511
8797
|
refreshContextUsage();
|
|
8512
8798
|
const studioMode = normalizeStudioUiMode(requestUrl.searchParams.get("mode"));
|
|
8513
8799
|
const requestInitialDocument = resolveRequestedStudioDocumentFromUrl(requestUrl, initialStudioDocument, studioCwd, lastStudioResponse);
|
|
8514
|
-
res.end(buildStudioHtml(requestInitialDocument, serverState.token, lastCommandCtx?.ui.theme, currentModelLabel, terminalSessionLabel, contextUsageSnapshot, studioMode));
|
|
8800
|
+
res.end(buildStudioHtml(requestInitialDocument, serverState.token, lastCommandCtx?.ui.theme, currentModelLabel, terminalSessionLabel, terminalSessionDetail, contextUsageSnapshot, studioMode));
|
|
8515
8801
|
};
|
|
8516
8802
|
|
|
8517
8803
|
const ensureServer = async (): Promise<StudioServerState> => {
|