osdy-pi 0.1.8 → 1.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 +209 -161
- package/bin/osdy-pi.mjs +127 -0
- package/extensions/osdy-pi/account-profiles.test.ts +517 -0
- package/extensions/osdy-pi/account-profiles.ts +581 -0
- package/extensions/osdy-pi/animation.ts +47 -5
- package/extensions/osdy-pi/constants.ts +282 -103
- package/extensions/osdy-pi/diff-panel.ts +78 -24
- package/extensions/osdy-pi/editor-settings.test.ts +163 -0
- package/extensions/osdy-pi/editor-settings.ts +112 -0
- package/extensions/osdy-pi/metrics.ts +8 -135
- package/extensions/osdy-pi/plugin-events.ts +26 -0
- package/extensions/osdy-pi/profile-label.ts +29 -0
- package/extensions/osdy-pi/runtime-helpers.test.ts +26 -0
- package/extensions/osdy-pi/runtime-helpers.ts +87 -44
- package/extensions/osdy-pi/runtime.test.ts +62 -0
- package/extensions/osdy-pi/runtime.ts +256 -64
- package/extensions/osdy-pi/types.ts +33 -10
- package/extensions/osdy-pi/ui.test.ts +51 -0
- package/extensions/osdy-pi/ui.ts +298 -114
- package/extensions/osdy-pi/utils.ts +43 -1
- package/extensions/osdy-pi/working-tree.ts +25 -6
- package/package.json +22 -5
- package/scripts/osdy-pi-account-profiles.mjs +501 -0
- package/themes/osdy-pi-catppuccin-frappe.json +94 -0
- package/themes/osdy-pi-catppuccin-latte.json +94 -0
- package/themes/osdy-pi-catppuccin-macchiato.json +94 -0
- package/themes/osdy-pi-catppuccin-mocha.json +94 -0
- package/themes/osdy-pi-dark.json +0 -6
- package/themes/osdy-pi-dracula.json +83 -0
- package/themes/osdy-pi-kanagawa-dragon.json +91 -0
- package/themes/osdy-pi-kanagawa-lotus.json +92 -0
- package/themes/osdy-pi-kanagawa-wave.json +91 -0
- package/themes/osdy-pi-lucent-orange.json +80 -0
- package/themes/osdy-pi-matrix.json +83 -0
- package/themes/osdy-pi-new.json +91 -0
- package/themes/osdy-pi-sexy.json +84 -0
- package/themes/osdy-pi-tokyo-night.json +83 -0
- package/themes/osdy-pi-light.json +0 -84
- package/themes/osdy-pi-simple.json +0 -83
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
2
2
|
import type { HeaderVariant } from "./types.js";
|
|
3
3
|
|
|
4
|
-
export const THEME_NAME = "osdy-pi-dark";
|
|
5
4
|
export const ANIMATION_ENABLED = true;
|
|
6
5
|
export const ANIMATION_INTERVAL_MS = 30;
|
|
7
6
|
export const INTRO_ANIMATION_FRAMES = 28;
|
|
@@ -19,8 +18,11 @@ export const WORKING_SPINNER_FRAMES = [
|
|
|
19
18
|
];
|
|
20
19
|
export const WORKING_WIDGET_KEY = "osdy-pi-working";
|
|
21
20
|
export const WORKING_TREE_WIDGET_KEY = "osdy-pi-working-tree";
|
|
22
|
-
export const MASCOT_MIN_ROWS = 34;
|
|
23
21
|
export const MASCOT_GAP = 0;
|
|
22
|
+
export const COMPACT_HEADER_HIDDEN_COLUMNS = 72;
|
|
23
|
+
export const STACKED_CONTENT_MAX_ROWS = 32;
|
|
24
|
+
export const STACKED_HEADER_MAX_ROWS = 8;
|
|
25
|
+
export const STACKED_HEADER_MIN_ROWS = 4;
|
|
24
26
|
|
|
25
27
|
const HTML_MASCOT = [
|
|
26
28
|
" ▓▓▓▓▒ ",
|
|
@@ -81,11 +83,11 @@ const HTML_MASCOT_MAP = [
|
|
|
81
83
|
" llllmddmddddddddhhhhhhhhhhlhhhhhhhlllmmmm ",
|
|
82
84
|
" hllllmddddddmhhhhhhhhhhhhhllllllmmmh ",
|
|
83
85
|
" dmmmmmmmmlllllllllllllmdmmm ",
|
|
84
|
-
" mmmmmmmmmmmmmmmlllmm
|
|
86
|
+
" mmmmmmmmmmmmmmmlllmm m v c p ",
|
|
85
87
|
" dlmmmmllllmmlmmllllllm dmllllmmmmmmmm ",
|
|
86
88
|
" lllllmmlllllllllllllmmm lmllllllllhlmmmmm ",
|
|
87
89
|
" mlllllmdmlllllllllmmmd mddmmmmlhhhlllmmmmm ",
|
|
88
|
-
"
|
|
90
|
+
" hhmmmmmmdmdmmmmmmmmmmm dddmmmmmmmmlllllmmdmm",
|
|
89
91
|
" mmmlllllhllllmmmmmmmm dmmllllmmmmmmmlllmdm ",
|
|
90
92
|
" mlllhhhhhhhhlllllllmmdddmlllhllmmmmmmlmml ",
|
|
91
93
|
" llllllhhhhhhhllllllllmmdddmmlllllmmmmdmmd ",
|
|
@@ -132,7 +134,19 @@ type HeaderPalette = {
|
|
|
132
134
|
trailColor: string;
|
|
133
135
|
};
|
|
134
136
|
|
|
135
|
-
export
|
|
137
|
+
export const MASCOT_TONE_KEYS = {
|
|
138
|
+
background: "b",
|
|
139
|
+
bright: "h",
|
|
140
|
+
light: "l",
|
|
141
|
+
mid: "m",
|
|
142
|
+
dark: "d",
|
|
143
|
+
rightEdgeCyan: "p",
|
|
144
|
+
rightEdgeViolet: "c",
|
|
145
|
+
rightEdgeSilver: "v",
|
|
146
|
+
} as const;
|
|
147
|
+
|
|
148
|
+
export type MascotToneKey =
|
|
149
|
+
(typeof MASCOT_TONE_KEYS)[keyof typeof MASCOT_TONE_KEYS];
|
|
136
150
|
|
|
137
151
|
export type MascotTonePalette = Record<MascotToneKey, string>;
|
|
138
152
|
|
|
@@ -141,9 +155,6 @@ type HeaderVariantConfig = {
|
|
|
141
155
|
header: readonly string[];
|
|
142
156
|
headerMap?: readonly string[];
|
|
143
157
|
headerTonePalette?: MascotTonePalette;
|
|
144
|
-
minRowsForFull?: number;
|
|
145
|
-
fallbackHeader?: readonly string[];
|
|
146
|
-
fallbackLinePalette?: (lineIndex: number) => HeaderPalette;
|
|
147
158
|
mascot?: readonly string[];
|
|
148
159
|
mascotMap?: readonly string[];
|
|
149
160
|
linePalette: (lineIndex: number) => HeaderPalette;
|
|
@@ -160,22 +171,22 @@ const CLASSIC_PALETTE: HeaderPalette = {
|
|
|
160
171
|
const CLASSIC_LINK_PALETTE: HeaderPalette = {
|
|
161
172
|
baseColor: "mdLink",
|
|
162
173
|
highlightColor: "mdHeading",
|
|
163
|
-
trailColor: "
|
|
174
|
+
trailColor: "accent",
|
|
164
175
|
};
|
|
165
176
|
|
|
166
|
-
const
|
|
177
|
+
const OSDY_THEME_CYAN_PALETTE: HeaderPalette = {
|
|
167
178
|
baseColor: "accent",
|
|
168
179
|
highlightColor: "mdHeading",
|
|
169
180
|
trailColor: "mdLink",
|
|
170
181
|
};
|
|
171
182
|
|
|
172
|
-
const
|
|
183
|
+
const OSDY_THEME_SILVER_PALETTE: HeaderPalette = {
|
|
173
184
|
baseColor: "mdLink",
|
|
174
185
|
highlightColor: "accent",
|
|
175
186
|
trailColor: "mdHeading",
|
|
176
187
|
};
|
|
177
188
|
|
|
178
|
-
const
|
|
189
|
+
const OSDY_THEME_VIOLET_PALETTE: HeaderPalette = {
|
|
179
190
|
baseColor: "mdHeading",
|
|
180
191
|
highlightColor: "mdLink",
|
|
181
192
|
trailColor: "accent",
|
|
@@ -188,21 +199,137 @@ const OSDY_THEME_MASCOT_PALETTE: HeaderPalette = {
|
|
|
188
199
|
};
|
|
189
200
|
|
|
190
201
|
const HTML_MASCOT_TONES: MascotTonePalette = {
|
|
191
|
-
b: "
|
|
192
|
-
h: "
|
|
193
|
-
l: "
|
|
194
|
-
m: "
|
|
195
|
-
d: "
|
|
196
|
-
p: "
|
|
197
|
-
c: "
|
|
198
|
-
v: "
|
|
202
|
+
b: "#F9F7F2",
|
|
203
|
+
h: "#F0E5D7",
|
|
204
|
+
l: "#C7B4A1",
|
|
205
|
+
m: "#7D6F67",
|
|
206
|
+
d: "#2A2321",
|
|
207
|
+
p: "accent",
|
|
208
|
+
c: "mdHeading",
|
|
209
|
+
v: "mdLink",
|
|
199
210
|
};
|
|
200
211
|
|
|
201
|
-
|
|
212
|
+
const RAW_BODY_TONE_KEYS = [
|
|
213
|
+
MASCOT_TONE_KEYS.background,
|
|
214
|
+
MASCOT_TONE_KEYS.bright,
|
|
215
|
+
MASCOT_TONE_KEYS.light,
|
|
216
|
+
MASCOT_TONE_KEYS.mid,
|
|
217
|
+
MASCOT_TONE_KEYS.dark,
|
|
218
|
+
] as const;
|
|
219
|
+
const RAW_HEX_COLOR = /^#[0-9A-F]{6}$/;
|
|
220
|
+
|
|
221
|
+
function assertRawHexPalette(palette: MascotTonePalette): void {
|
|
222
|
+
for (const tone of RAW_BODY_TONE_KEYS) {
|
|
223
|
+
if (!RAW_HEX_COLOR.test(palette[tone])) {
|
|
224
|
+
throw new Error(`Mascot tone ${tone} must be a #RRGGBB color.`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export type MascotArt = {
|
|
202
230
|
mascot: readonly string[];
|
|
203
231
|
toneMap: readonly string[];
|
|
204
232
|
};
|
|
205
233
|
|
|
234
|
+
function mascotCodePointWidth(mascot: readonly string[]): number {
|
|
235
|
+
return Math.max(...mascot.map((line) => Array.from(line).length));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function trimMascotMargins(art: MascotArt): MascotArt {
|
|
239
|
+
const rowWidth = Math.max(
|
|
240
|
+
mascotCodePointWidth(art.mascot),
|
|
241
|
+
mascotCodePointWidth(art.toneMap),
|
|
242
|
+
);
|
|
243
|
+
const rows = art.mascot.map((line, index) => ({
|
|
244
|
+
mascot: Array.from(line.padEnd(rowWidth, " ")),
|
|
245
|
+
toneMap: Array.from((art.toneMap[index] ?? "").padEnd(rowWidth, " ")),
|
|
246
|
+
}));
|
|
247
|
+
const occupied = (rowIndex: number, columnIndex: number): boolean => {
|
|
248
|
+
const row = rows[rowIndex];
|
|
249
|
+
return (
|
|
250
|
+
row?.mascot[columnIndex] !== " " || row?.toneMap[columnIndex] !== " "
|
|
251
|
+
);
|
|
252
|
+
};
|
|
253
|
+
const rowIndexes = rows
|
|
254
|
+
.map((_row, rowIndex) => rowIndex)
|
|
255
|
+
.filter((rowIndex) =>
|
|
256
|
+
Array.from({ length: rowWidth }, (_value, columnIndex) =>
|
|
257
|
+
occupied(rowIndex, columnIndex),
|
|
258
|
+
).some(Boolean),
|
|
259
|
+
);
|
|
260
|
+
const columnIndexes = Array.from(
|
|
261
|
+
{ length: rowWidth },
|
|
262
|
+
(_value, columnIndex) =>
|
|
263
|
+
rows.some((_row, rowIndex) => occupied(rowIndex, columnIndex)),
|
|
264
|
+
);
|
|
265
|
+
const firstRow = rowIndexes[0] ?? 0;
|
|
266
|
+
const lastRow = rowIndexes.at(-1) ?? firstRow;
|
|
267
|
+
const firstColumn = columnIndexes.findIndex(Boolean);
|
|
268
|
+
const lastColumn = columnIndexes.lastIndexOf(true);
|
|
269
|
+
if (firstColumn < 0 || lastColumn < firstColumn) return art;
|
|
270
|
+
return {
|
|
271
|
+
mascot: rows
|
|
272
|
+
.slice(firstRow, lastRow + 1)
|
|
273
|
+
.map((row) => row.mascot.slice(firstColumn, lastColumn + 1).join("")),
|
|
274
|
+
toneMap: rows
|
|
275
|
+
.slice(firstRow, lastRow + 1)
|
|
276
|
+
.map((row) => row.toneMap.slice(firstColumn, lastColumn + 1).join("")),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function isMascotToneKey(value: string): value is MascotToneKey {
|
|
281
|
+
return (
|
|
282
|
+
value === MASCOT_TONE_KEYS.background ||
|
|
283
|
+
value === MASCOT_TONE_KEYS.bright ||
|
|
284
|
+
value === MASCOT_TONE_KEYS.light ||
|
|
285
|
+
value === MASCOT_TONE_KEYS.mid ||
|
|
286
|
+
value === MASCOT_TONE_KEYS.dark ||
|
|
287
|
+
value === MASCOT_TONE_KEYS.rightEdgeCyan ||
|
|
288
|
+
value === MASCOT_TONE_KEYS.rightEdgeViolet ||
|
|
289
|
+
value === MASCOT_TONE_KEYS.rightEdgeSilver
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function assertMascotStructure(
|
|
294
|
+
art: MascotArt,
|
|
295
|
+
palette: MascotTonePalette,
|
|
296
|
+
): void {
|
|
297
|
+
if (art.mascot.length === 0) {
|
|
298
|
+
throw new Error("Mascot art must contain at least one row.");
|
|
299
|
+
}
|
|
300
|
+
if (art.mascot.length !== art.toneMap.length) {
|
|
301
|
+
throw new Error("Mascot art and tone map must have the same row count.");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
for (let rowIndex = 0; rowIndex < art.mascot.length; rowIndex += 1) {
|
|
305
|
+
const mascotLine = art.mascot[rowIndex] ?? "";
|
|
306
|
+
const toneLine = art.toneMap[rowIndex] ?? "";
|
|
307
|
+
const mascotCharacters = Array.from(mascotLine);
|
|
308
|
+
const toneCharacters = Array.from(toneLine);
|
|
309
|
+
if (mascotCharacters.length !== toneCharacters.length) {
|
|
310
|
+
throw new Error(`Mascot row ${rowIndex} and its tone map width differ.`);
|
|
311
|
+
}
|
|
312
|
+
for (
|
|
313
|
+
let columnIndex = 0;
|
|
314
|
+
columnIndex < toneCharacters.length;
|
|
315
|
+
columnIndex += 1
|
|
316
|
+
) {
|
|
317
|
+
const tone = toneCharacters[columnIndex] ?? " ";
|
|
318
|
+
const character = mascotCharacters[columnIndex] ?? " ";
|
|
319
|
+
if (tone !== " " && !isMascotToneKey(tone)) {
|
|
320
|
+
throw new Error(
|
|
321
|
+
`Mascot row ${rowIndex} has an invalid tone key: ${tone}.`,
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
if (character !== " " && (!isMascotToneKey(tone) || !palette[tone])) {
|
|
325
|
+
throw new Error(
|
|
326
|
+
`Mascot row ${rowIndex} column ${columnIndex} has no usable tone.`,
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
206
333
|
function sampleIndex(
|
|
207
334
|
outputIndex: number,
|
|
208
335
|
outputCount: number,
|
|
@@ -215,94 +342,113 @@ function sampleIndex(
|
|
|
215
342
|
);
|
|
216
343
|
}
|
|
217
344
|
|
|
218
|
-
function
|
|
219
|
-
line
|
|
220
|
-
outputWidth: number,
|
|
221
|
-
inputWidth: number,
|
|
222
|
-
): string {
|
|
223
|
-
const chars = Array.from(line.padEnd(inputWidth, " "));
|
|
345
|
+
function scaleLine(line: string, outputWidth: number): string {
|
|
346
|
+
const characters = Array.from(line);
|
|
224
347
|
return Array.from({ length: outputWidth }, (_value, outputIndex) => {
|
|
225
|
-
const inputIndex = sampleIndex(outputIndex, outputWidth,
|
|
226
|
-
return
|
|
348
|
+
const inputIndex = sampleIndex(outputIndex, outputWidth, characters.length);
|
|
349
|
+
return characters[inputIndex] ?? " ";
|
|
227
350
|
}).join("");
|
|
228
351
|
}
|
|
229
352
|
|
|
230
|
-
function
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
(accumulator, line) => ({
|
|
251
|
-
mascot: [...accumulator.mascot, line.mascot],
|
|
252
|
-
toneMap: [...accumulator.toneMap, line.toneMap],
|
|
353
|
+
function addRightEdgeGlow(art: MascotArt): MascotArt {
|
|
354
|
+
return {
|
|
355
|
+
mascot: art.mascot,
|
|
356
|
+
toneMap: art.toneMap.map((lineMap, lineIndex) => {
|
|
357
|
+
const tones = Array.from(lineMap);
|
|
358
|
+
const mascotLine = Array.from(art.mascot[lineIndex] ?? "");
|
|
359
|
+
let glowIndex = 0;
|
|
360
|
+
for (
|
|
361
|
+
let index = mascotLine.length - 1;
|
|
362
|
+
index >= 0 && glowIndex < 3;
|
|
363
|
+
index -= 1
|
|
364
|
+
) {
|
|
365
|
+
if (mascotLine[index] !== " ") {
|
|
366
|
+
const glowTone = ["p", "c", "v"] as const;
|
|
367
|
+
tones[index] =
|
|
368
|
+
glowTone[glowIndex] ?? MASCOT_TONE_KEYS.rightEdgeSilver;
|
|
369
|
+
glowIndex += 1;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return tones.join("");
|
|
253
373
|
}),
|
|
254
|
-
|
|
255
|
-
);
|
|
374
|
+
};
|
|
256
375
|
}
|
|
257
376
|
|
|
258
|
-
|
|
259
|
-
mascot:
|
|
260
|
-
toneMap:
|
|
261
|
-
)
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
) {
|
|
281
|
-
chars[index] = glowTone;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
return chars.join("");
|
|
285
|
-
});
|
|
377
|
+
const ROSE_MASCOT = addRightEdgeGlow({
|
|
378
|
+
mascot: HTML_MASCOT,
|
|
379
|
+
toneMap: HTML_MASCOT_MAP,
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
assertRawHexPalette(HTML_MASCOT_TONES);
|
|
383
|
+
assertMascotStructure(ROSE_MASCOT, HTML_MASCOT_TONES);
|
|
384
|
+
|
|
385
|
+
type PreparedMascotArt = {
|
|
386
|
+
art: MascotArt;
|
|
387
|
+
width: number;
|
|
388
|
+
rows: number;
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
function prepareMascotArt(art: MascotArt): PreparedMascotArt {
|
|
392
|
+
assertMascotStructure(art, HTML_MASCOT_TONES);
|
|
393
|
+
const trimmed = trimMascotMargins(art);
|
|
394
|
+
return {
|
|
395
|
+
art: trimmed,
|
|
396
|
+
width: mascotCodePointWidth(trimmed.mascot),
|
|
397
|
+
rows: trimmed.mascot.length,
|
|
398
|
+
};
|
|
286
399
|
}
|
|
287
400
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
);
|
|
401
|
+
export function mascotWidthForRows(
|
|
402
|
+
art: MascotArt,
|
|
403
|
+
maximumRows: number,
|
|
404
|
+
): number {
|
|
405
|
+
const prepared = prepareMascotArt(art);
|
|
406
|
+
const heightScale = Math.min(
|
|
407
|
+
1,
|
|
408
|
+
Math.max(1, Math.floor(maximumRows)) / prepared.rows,
|
|
409
|
+
);
|
|
410
|
+
return Math.max(1, Math.floor(prepared.width * heightScale));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export function scaleMascot(
|
|
414
|
+
art: MascotArt,
|
|
415
|
+
maximumWidth: number,
|
|
416
|
+
maximumRows: number,
|
|
417
|
+
): MascotArt {
|
|
418
|
+
const prepared = prepareMascotArt(art);
|
|
419
|
+
const scale = Math.min(
|
|
420
|
+
1,
|
|
421
|
+
Math.max(1, Math.floor(maximumWidth)) / prepared.width,
|
|
422
|
+
Math.max(1, Math.floor(maximumRows)) / prepared.rows,
|
|
423
|
+
);
|
|
424
|
+
const outputWidth = Math.max(1, Math.floor(prepared.width * scale));
|
|
425
|
+
const outputRows = Math.max(1, Math.floor(prepared.rows * scale));
|
|
426
|
+
const scaled = {
|
|
427
|
+
mascot: Array.from({ length: outputRows }, (_value, outputIndex) => {
|
|
428
|
+
const inputIndex = sampleIndex(outputIndex, outputRows, prepared.rows);
|
|
429
|
+
return scaleLine(prepared.art.mascot[inputIndex] ?? "", outputWidth);
|
|
430
|
+
}),
|
|
431
|
+
toneMap: Array.from({ length: outputRows }, (_value, outputIndex) => {
|
|
432
|
+
const inputIndex = sampleIndex(outputIndex, outputRows, prepared.rows);
|
|
433
|
+
return scaleLine(prepared.art.toneMap[inputIndex] ?? "", outputWidth);
|
|
434
|
+
}),
|
|
435
|
+
};
|
|
436
|
+
assertMascotStructure(scaled, HTML_MASCOT_TONES);
|
|
437
|
+
return scaled;
|
|
438
|
+
}
|
|
293
439
|
|
|
294
440
|
function osdyThemePalette(lineIndex: number): HeaderPalette {
|
|
295
|
-
if (lineIndex < 4) return
|
|
296
|
-
if (lineIndex < 8) return
|
|
297
|
-
return
|
|
441
|
+
if (lineIndex < 4) return OSDY_THEME_CYAN_PALETTE;
|
|
442
|
+
if (lineIndex < 8) return OSDY_THEME_SILVER_PALETTE;
|
|
443
|
+
return OSDY_THEME_VIOLET_PALETTE;
|
|
298
444
|
}
|
|
299
445
|
|
|
300
446
|
export const HEADER_VARIANTS: Record<HeaderVariant, HeaderVariantConfig> = {
|
|
301
447
|
"osdy-theme": {
|
|
302
448
|
label: "OsdyTheme",
|
|
303
449
|
header: HEADER_SIMPLE,
|
|
304
|
-
mascot:
|
|
305
|
-
mascotMap:
|
|
450
|
+
mascot: ROSE_MASCOT.mascot,
|
|
451
|
+
mascotMap: ROSE_MASCOT.toneMap,
|
|
306
452
|
linePalette: osdyThemePalette,
|
|
307
453
|
mascotPalette: OSDY_THEME_MASCOT_PALETTE,
|
|
308
454
|
mascotTonePalette: HTML_MASCOT_TONES,
|
|
@@ -310,8 +456,8 @@ export const HEADER_VARIANTS: Record<HeaderVariant, HeaderVariantConfig> = {
|
|
|
310
456
|
classic: {
|
|
311
457
|
label: "Classic",
|
|
312
458
|
header: HEADER_CLASSIC,
|
|
313
|
-
mascot:
|
|
314
|
-
mascotMap:
|
|
459
|
+
mascot: ROSE_MASCOT.mascot,
|
|
460
|
+
mascotMap: ROSE_MASCOT.toneMap,
|
|
315
461
|
linePalette: (lineIndex) =>
|
|
316
462
|
lineIndex >= 6 ? CLASSIC_LINK_PALETTE : CLASSIC_PALETTE,
|
|
317
463
|
mascotPalette: OSDY_THEME_MASCOT_PALETTE,
|
|
@@ -319,6 +465,12 @@ export const HEADER_VARIANTS: Record<HeaderVariant, HeaderVariantConfig> = {
|
|
|
319
465
|
},
|
|
320
466
|
};
|
|
321
467
|
|
|
468
|
+
export type ScaledHeaderArt = {
|
|
469
|
+
header: readonly string[];
|
|
470
|
+
toneMap: readonly string[] | undefined;
|
|
471
|
+
sourceIndexes: number[];
|
|
472
|
+
};
|
|
473
|
+
|
|
322
474
|
export function headerWidth(variant: HeaderVariant): number {
|
|
323
475
|
return HEADER_VARIANTS[variant].header.reduce(
|
|
324
476
|
(maxWidth, line) => Math.max(maxWidth, visibleWidth(line)),
|
|
@@ -326,14 +478,41 @@ export function headerWidth(variant: HeaderVariant): number {
|
|
|
326
478
|
);
|
|
327
479
|
}
|
|
328
480
|
|
|
329
|
-
export function
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
481
|
+
export function scaleHeader(
|
|
482
|
+
variant: HeaderVariant,
|
|
483
|
+
maximumWidth: number,
|
|
484
|
+
maximumRows: number,
|
|
485
|
+
): ScaledHeaderArt {
|
|
486
|
+
const source = HEADER_VARIANTS[variant];
|
|
487
|
+
const inputWidth = headerWidth(variant);
|
|
488
|
+
const inputRows = source.header.length;
|
|
489
|
+
if (maximumWidth >= inputWidth && maximumRows >= inputRows) {
|
|
490
|
+
return {
|
|
491
|
+
header: source.header,
|
|
492
|
+
toneMap: source.headerMap,
|
|
493
|
+
sourceIndexes: source.header.map((_line, index) => index),
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
const scale = Math.min(
|
|
497
|
+
1,
|
|
498
|
+
Math.max(1, Math.floor(maximumWidth)) / inputWidth,
|
|
499
|
+
Math.max(1, Math.floor(maximumRows)) / inputRows,
|
|
333
500
|
);
|
|
501
|
+
const outputWidth = Math.max(1, Math.floor(inputWidth * scale));
|
|
502
|
+
const outputRows = Math.max(1, Math.floor(inputRows * scale));
|
|
503
|
+
const sourceIndexes = Array.from(
|
|
504
|
+
{ length: outputRows },
|
|
505
|
+
(_value, outputIndex) => sampleIndex(outputIndex, outputRows, inputRows),
|
|
506
|
+
);
|
|
507
|
+
return {
|
|
508
|
+
header: sourceIndexes.map((sourceIndex) =>
|
|
509
|
+
scaleLine(source.header[sourceIndex] ?? "", outputWidth),
|
|
510
|
+
),
|
|
511
|
+
toneMap: source.headerMap
|
|
512
|
+
? sourceIndexes.map((sourceIndex) =>
|
|
513
|
+
scaleLine(source.headerMap?.[sourceIndex] ?? "", outputWidth),
|
|
514
|
+
)
|
|
515
|
+
: undefined,
|
|
516
|
+
sourceIndexes,
|
|
517
|
+
};
|
|
334
518
|
}
|
|
335
|
-
|
|
336
|
-
export const HEADER_FALLBACK = [
|
|
337
|
-
"OSDY - PI",
|
|
338
|
-
"</> Compilador de ideas",
|
|
339
|
-
] as const;
|