@codersbrew/pi-tools 0.1.0 → 0.1.1

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.
@@ -25,7 +25,6 @@ import {
25
25
  truncateToWidth,
26
26
  visibleWidth,
27
27
  } from "@mariozechner/pi-tui";
28
- import { sliceByColumn } from "@mariozechner/pi-tui/dist/utils.js";
29
28
  import os from "node:os";
30
29
  import path from "node:path";
31
30
  import fs from "node:fs/promises";
@@ -48,6 +47,78 @@ const TOD_BUCKETS: { key: TodKey; label: string; from: number; to: number }[] =
48
47
  { key: "night", label: "Night (22–23)", from: 22, to: 23 },
49
48
  ];
50
49
 
50
+ const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
51
+
52
+ function extractAnsiCode(str: string, pos: number): { code: string; length: number } | null {
53
+ if (pos >= str.length || str[pos] !== "\x1b") return null;
54
+ const next = str[pos + 1];
55
+
56
+ if (next === "[") {
57
+ let j = pos + 2;
58
+ while (j < str.length && !/[mGKHJ]/.test(str[j])) j++;
59
+ if (j < str.length) return { code: str.substring(pos, j + 1), length: j + 1 - pos };
60
+ return null;
61
+ }
62
+
63
+ if (next === "]" || next === "_") {
64
+ let j = pos + 2;
65
+ while (j < str.length) {
66
+ if (str[j] === "\x07") return { code: str.substring(pos, j + 1), length: j + 1 - pos };
67
+ if (str[j] === "\x1b" && str[j + 1] === "\\") return { code: str.substring(pos, j + 2), length: j + 2 - pos };
68
+ j++;
69
+ }
70
+ return null;
71
+ }
72
+
73
+ return null;
74
+ }
75
+
76
+ // pi's extension loader aliases @mariozechner/pi-tui to its dist/index.js entry.
77
+ // Importing deep internals like @mariozechner/pi-tui/dist/utils.js therefore resolves
78
+ // incorrectly under jiti (…/dist/index.js/dist/utils.js), so keep this local helper.
79
+ function sliceByColumn(line: string, startCol: number, length: number, strict = false): string {
80
+ if (length <= 0) return "";
81
+
82
+ const endCol = startCol + length;
83
+ let result = "";
84
+ let currentCol = 0;
85
+ let i = 0;
86
+ let pendingAnsi = "";
87
+
88
+ while (i < line.length) {
89
+ const ansi = extractAnsiCode(line, i);
90
+ if (ansi) {
91
+ if (currentCol >= startCol && currentCol < endCol) result += ansi.code;
92
+ else if (currentCol < startCol) pendingAnsi += ansi.code;
93
+ i += ansi.length;
94
+ continue;
95
+ }
96
+
97
+ let textEnd = i;
98
+ while (textEnd < line.length && !extractAnsiCode(line, textEnd)) textEnd++;
99
+
100
+ for (const { segment } of graphemeSegmenter.segment(line.slice(i, textEnd))) {
101
+ const w = visibleWidth(segment);
102
+ const inRange = currentCol >= startCol && currentCol < endCol;
103
+ const fits = !strict || currentCol + w <= endCol;
104
+ if (inRange && fits) {
105
+ if (pendingAnsi) {
106
+ result += pendingAnsi;
107
+ pendingAnsi = "";
108
+ }
109
+ result += segment;
110
+ }
111
+ currentCol += w;
112
+ if (currentCol >= endCol) break;
113
+ }
114
+
115
+ i = textEnd;
116
+ if (currentCol >= endCol) break;
117
+ }
118
+
119
+ return result;
120
+ }
121
+
51
122
  function todBucketForHour(hour: number): TodKey {
52
123
  for (const b of TOD_BUCKETS) {
53
124
  if (hour >= b.from && hour <= b.to) return b.key;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codersbrew/pi-tools",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A pi package bundling CodersBrew pi extensions.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -29,9 +29,10 @@
29
29
  "node": ">=20.6.0"
30
30
  },
31
31
  "scripts": {
32
+ "test": "node --test test/*.test.mjs",
32
33
  "typecheck": "tsc --noEmit",
33
34
  "pack:check": "npm pack --dry-run",
34
- "check": "npm run typecheck && npm run pack:check"
35
+ "check": "npm run test && npm run typecheck && npm run pack:check"
35
36
  },
36
37
  "publishConfig": {
37
38
  "access": "public"