@tenyokjs/devdevian-cli 0.1.0-alpha.1 → 0.3.0-alpha.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.
@@ -0,0 +1,168 @@
1
+ import stringWidth from "string-width";
2
+ import wrapAnsi from "wrap-ansi";
3
+ import { clean } from "./client.mjs";
4
+ const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
5
+ export const graphemes = (value) =>
6
+ Array.from(segmenter.segment(value), (part) => part.segment);
7
+ export function clip(value, width) {
8
+ const text = clean(value).replace(/\n/g, " ");
9
+ if (width <= 0) return "";
10
+ if (stringWidth(text) <= width) return text;
11
+ let result = "";
12
+ for (const part of graphemes(text)) {
13
+ if (stringWidth(result + part) > width - 1) break;
14
+ result += part;
15
+ }
16
+ return result + "…";
17
+ }
18
+ export function wrap(value, width) {
19
+ return wrapAnsi(clean(value).replace(/\t/g, " "), Math.max(1, width), {
20
+ hard: true,
21
+ trim: false,
22
+ }).split("\n");
23
+ }
24
+ export function editText(
25
+ value,
26
+ cursor,
27
+ input,
28
+ key,
29
+ { multiline = false, max = 15000 } = {},
30
+ ) {
31
+ const chars = graphemes(value),
32
+ at = Math.max(0, Math.min(cursor, chars.length));
33
+ if (multiline && (key.upArrow || key.downArrow)) {
34
+ const start = chars.slice(0, at).lastIndexOf("\n") + 1,
35
+ column = at - start;
36
+ if (key.upArrow) {
37
+ if (!start) return { value, cursor: 0 };
38
+ const previous = chars.slice(0, start - 1).lastIndexOf("\n") + 1;
39
+ return { value, cursor: Math.min(start - 1, previous + column) };
40
+ }
41
+ const end = chars.indexOf("\n", at);
42
+ if (end < 0) return { value, cursor: chars.length };
43
+ const next = chars.indexOf("\n", end + 1);
44
+ return {
45
+ value,
46
+ cursor: Math.min(next < 0 ? chars.length : next, end + 1 + column),
47
+ };
48
+ }
49
+ if (key.leftArrow) return { value, cursor: Math.max(0, at - 1) };
50
+ if (key.rightArrow) return { value, cursor: Math.min(chars.length, at + 1) };
51
+ if (key.home || (key.ctrl && input === "a")) {
52
+ const previous = chars.slice(0, at).lastIndexOf("\n");
53
+ return { value, cursor: previous + 1 };
54
+ }
55
+ if (key.end || (key.ctrl && input === "e")) {
56
+ const next = chars.indexOf("\n", at);
57
+ return { value, cursor: next < 0 ? chars.length : next };
58
+ }
59
+ if (key.ctrl && input === "u") {
60
+ let start = chars.slice(0, at).lastIndexOf("\n") + 1;
61
+ return {
62
+ value: [...chars.slice(0, start), ...chars.slice(at)].join(""),
63
+ cursor: start,
64
+ };
65
+ }
66
+ if (key.ctrl && input === "k") {
67
+ const end = chars.indexOf("\n", at);
68
+ return {
69
+ value: [...chars.slice(0, at), ...(end < 0 ? [] : chars.slice(end))].join(
70
+ "",
71
+ ),
72
+ cursor: at,
73
+ };
74
+ }
75
+ if (key.backspace) {
76
+ if (!at) return { value, cursor: at };
77
+ return {
78
+ value: [...chars.slice(0, at - 1), ...chars.slice(at)].join(""),
79
+ cursor: at - 1,
80
+ };
81
+ }
82
+ if (key.delete)
83
+ return {
84
+ value: [...chars.slice(0, at), ...chars.slice(at + 1)].join(""),
85
+ cursor: at,
86
+ };
87
+ if (key.ctrl || key.meta || key.escape || key.tab)
88
+ return { value, cursor: at };
89
+ let insertion = key.return
90
+ ? multiline
91
+ ? "\n"
92
+ : ""
93
+ : clean(input).replace(/\r/g, "");
94
+ if (!multiline) insertion = insertion.replace(/\n/g, " ");
95
+ if (!insertion) return { value, cursor: at };
96
+ const remaining = Math.max(0, max - value.length),
97
+ parts = [];
98
+ let length = 0;
99
+ for (const part of graphemes(insertion)) {
100
+ if (length + part.length > remaining) break;
101
+ parts.push(part);
102
+ length += part.length;
103
+ }
104
+ return {
105
+ value: [...chars.slice(0, at), ...parts, ...chars.slice(at)].join(""),
106
+ cursor: at + parts.length,
107
+ };
108
+ }
109
+ export function cursorLines(value, cursor, width) {
110
+ const parts = graphemes(value),
111
+ lines = wrap(
112
+ parts.slice(0, cursor).join("") + "▏" + parts.slice(cursor).join(""),
113
+ width,
114
+ );
115
+ const marker = lines.findIndex((line) => line.includes("▏"));
116
+ return { lines, line: Math.max(0, marker) };
117
+ }
118
+ export function parseCommand(text) {
119
+ const [cmd, ...rest] = text.trim().replace(/^\//, "").split(/\s+/);
120
+ const arg = rest.join(" ");
121
+ if (
122
+ [
123
+ "feed",
124
+ "inbox",
125
+ "communities",
126
+ "search",
127
+ "ask",
128
+ "review",
129
+ "help",
130
+ "devices",
131
+ "drafts",
132
+ ].includes(cmd)
133
+ )
134
+ return { command: cmd, arg };
135
+ if (
136
+ cmd === "open" &&
137
+ /^[1-9]\d*$/.test(arg) &&
138
+ Number.isSafeInteger(Number(arg))
139
+ )
140
+ return { command: cmd, arg: Number(arg) };
141
+ if (["quit", "logout"].includes(cmd)) return { command: cmd };
142
+ throw Error(
143
+ "Use /feed, /inbox, /search words, /open ID, /ask, /review, /communities, /devices, /drafts, /help or /quit.",
144
+ );
145
+ }
146
+ export class PageCache {
147
+ constructor({ max = 40, ttl = 30000 } = {}) {
148
+ this.rows = new Map();
149
+ this.max = max;
150
+ this.ttl = ttl;
151
+ }
152
+ get(key) {
153
+ const row = this.rows.get(key);
154
+ if (!row || Date.now() - row.time > this.ttl) return null;
155
+ this.rows.delete(key);
156
+ this.rows.set(key, row);
157
+ return row.value;
158
+ }
159
+ set(key, value) {
160
+ this.rows.delete(key);
161
+ this.rows.set(key, { value, time: Date.now() });
162
+ while (this.rows.size > this.max)
163
+ this.rows.delete(this.rows.keys().next().value);
164
+ }
165
+ clear() {
166
+ this.rows.clear();
167
+ }
168
+ }