clipwise 0.7.1 → 0.7.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/README.ko.md +2 -2
- package/README.md +34 -9
- package/dist/cli/index.js +224 -216
- package/dist/compose/frame-worker.js +58 -12
- package/dist/index.d.ts +312 -13
- package/dist/index.js +195 -70
- package/package.json +1 -1
- package/skills/clipwise.md +29 -1
|
@@ -460,6 +460,44 @@ import sharp5 from "sharp";
|
|
|
460
460
|
function escapeXml(s) {
|
|
461
461
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
462
462
|
}
|
|
463
|
+
function isCJK(ch) {
|
|
464
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
465
|
+
return code >= 4352 && code <= 4607 || // 한글 자모
|
|
466
|
+
code >= 11904 && code <= 40959 || // CJK 부수, 한자
|
|
467
|
+
code >= 44032 && code <= 55215 || // 한글 음절
|
|
468
|
+
code >= 63744 && code <= 64255 || // CJK 호환 한자
|
|
469
|
+
code >= 65072 && code <= 65103 || // CJK 호환 형태
|
|
470
|
+
code >= 65280 && code <= 65519 || // Full-width 문자
|
|
471
|
+
code >= 12288 && code <= 12543 || // CJK 기호, 히라가나, 가타카나
|
|
472
|
+
code >= 12784 && code <= 12799 || // 가타카나 확장
|
|
473
|
+
code >= 131072 && code <= 195103;
|
|
474
|
+
}
|
|
475
|
+
function displayWidth(text) {
|
|
476
|
+
let w = 0;
|
|
477
|
+
for (const ch of text) {
|
|
478
|
+
w += isCJK(ch) ? 1.7 : 1;
|
|
479
|
+
}
|
|
480
|
+
return w;
|
|
481
|
+
}
|
|
482
|
+
function wrapText(text, maxWidth) {
|
|
483
|
+
if (displayWidth(text) <= maxWidth) return [text];
|
|
484
|
+
const lines = [];
|
|
485
|
+
let current = "";
|
|
486
|
+
let currentWidth = 0;
|
|
487
|
+
for (const ch of text) {
|
|
488
|
+
const chWidth = isCJK(ch) ? 1.7 : 1;
|
|
489
|
+
if (currentWidth + chWidth > maxWidth && current.length > 0) {
|
|
490
|
+
lines.push(current);
|
|
491
|
+
current = ch;
|
|
492
|
+
currentWidth = chWidth;
|
|
493
|
+
} else {
|
|
494
|
+
current += ch;
|
|
495
|
+
currentWidth += chWidth;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
if (current.length > 0) lines.push(current);
|
|
499
|
+
return lines;
|
|
500
|
+
}
|
|
463
501
|
function buildSessions(keystrokes) {
|
|
464
502
|
const hasSessionIds = keystrokes.some((k) => k.sessionId !== void 0);
|
|
465
503
|
if (!hasSessionIds) {
|
|
@@ -493,16 +531,22 @@ async function renderKeystrokeHud(frameBuffer, keystrokes, frameTimestamp, confi
|
|
|
493
531
|
const lineGap = Math.round(fontSize * 0.45);
|
|
494
532
|
const charWidth = fontSize * 0.615;
|
|
495
533
|
const maxHudWidth = frameWidth - 60 * dpr;
|
|
496
|
-
const
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
534
|
+
const maxDisplayWidth = Math.max(10, (maxHudWidth - hudPadH * 2) / charWidth);
|
|
535
|
+
const wrappedLines = [];
|
|
536
|
+
sessions.forEach((text, sIdx) => {
|
|
537
|
+
const wrapped = wrapText(text, maxDisplayWidth);
|
|
538
|
+
for (const line of wrapped) {
|
|
539
|
+
wrappedLines.push({ text: line, sessionIdx: sIdx });
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
const lines = wrappedLines.map((l) => l.text);
|
|
543
|
+
const totalLineCount = lines.length;
|
|
544
|
+
const maxLineDisplayWidth = Math.max(...lines.map((l) => displayWidth(l)));
|
|
501
545
|
const hudWidth = Math.min(
|
|
502
|
-
Math.ceil(
|
|
546
|
+
Math.ceil(maxLineDisplayWidth * charWidth) + hudPadH * 2,
|
|
503
547
|
maxHudWidth
|
|
504
548
|
);
|
|
505
|
-
const hudHeight = Math.ceil(fontSize *
|
|
549
|
+
const hudHeight = Math.ceil(fontSize * totalLineCount + lineGap * (totalLineCount - 1) + hudPadV * 2);
|
|
506
550
|
const margin = 30 * dpr;
|
|
507
551
|
const hudY = frameHeight - hudHeight - margin;
|
|
508
552
|
let hudX;
|
|
@@ -517,18 +561,20 @@ async function renderKeystrokeHud(frameBuffer, keystrokes, frameTimestamp, confi
|
|
|
517
561
|
default:
|
|
518
562
|
hudX = Math.round((frameWidth - hudWidth) / 2);
|
|
519
563
|
}
|
|
520
|
-
const
|
|
521
|
-
const
|
|
564
|
+
const SESSION_OPACITY_FACTORS = [0.45, 0.7, 1];
|
|
565
|
+
const sessionOpacities = SESSION_OPACITY_FACTORS.slice(-lineCount);
|
|
522
566
|
const rx = (8 * dpr).toFixed(1);
|
|
523
567
|
const boxOp = (globalOpacity * 0.92).toFixed(3);
|
|
524
568
|
const textX = hudX + hudPadH;
|
|
525
569
|
const baselineY = hudY + hudPadV + fontSize * 0.82;
|
|
526
|
-
const textElements =
|
|
527
|
-
const
|
|
570
|
+
const textElements = wrappedLines.map(({ text, sessionIdx }, i) => {
|
|
571
|
+
const sessionPos = sessions.length <= 3 ? sessionIdx : sessionIdx - (sessions.length - 3);
|
|
572
|
+
const opFactor = sessionOpacities[Math.max(0, sessionPos)] ?? 1;
|
|
573
|
+
const op = (globalOpacity * opFactor).toFixed(3);
|
|
528
574
|
const lineY = baselineY + i * (fontSize + lineGap);
|
|
529
575
|
return `<text x="${textX}" y="${lineY}"
|
|
530
576
|
font-family="monospace, Menlo, Consolas" font-size="${fontSize}"
|
|
531
|
-
fill="${config.textColor}" opacity="${op}">${escapeXml(
|
|
577
|
+
fill="${config.textColor}" opacity="${op}">${escapeXml(text)}</text>`;
|
|
532
578
|
}).join("\n ");
|
|
533
579
|
const hudSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="${frameWidth}" height="${frameHeight}" shape-rendering="geometricPrecision" text-rendering="geometricPrecision">
|
|
534
580
|
<rect x="${hudX}" y="${hudY}" width="${hudWidth}" height="${hudHeight}"
|