ubugeeei 2.0.0 → 3.1.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 +79 -17
- package/dist/cli.mjs +405 -0
- package/package.json +26 -31
- package/assets/cover.png +0 -0
- package/dist/cli.js +0 -182
- package/index.js +0 -2
package/README.md
CHANGED
|
@@ -1,25 +1,87 @@
|
|
|
1
|
-
|
|
2
|
-
<p>🇯🇵🗼 software engineer / 🦀彡..。o うに,禅/サムライ 🥷</p>
|
|
1
|
+
# UBUGEEEI(1)
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
king of <a href="https://github.com/chibivue-land">@chibivue-land</a><br>
|
|
3
|
+
## NAME
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
`ubugeeei` - software engineer
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
writing: <a href="https://wtrclred.io" target="_blank">wtrclred.io</a> (blog), <a href="https://github.com/ubugeei/reading-vuejs-core-vapor" target="_blank">reading-vuejs-core-vapor</a>,
|
|
7
|
+
## SYNOPSIS
|
|
13
8
|
|
|
14
|
-
|
|
15
|
-
```shell
|
|
16
|
-
npx ubugeeei
|
|
17
|
-
```
|
|
9
|
+
`npx ubugeeei`
|
|
18
10
|
|
|
19
|
-
|
|
11
|
+
## PROFILE
|
|
20
12
|
|
|
21
|
-
|
|
13
|
+
- もののけ王
|
|
14
|
+
- うにをくらえ ✹ 🦀 🦆 彡..。o
|
|
15
|
+
- Tokyo 🇯🇵
|
|
16
|
+
- 発言は全て現代アートだよ( ◠‿◠ )
|
|
22
17
|
|
|
23
|
-
|
|
18
|
+
## ROLES
|
|
24
19
|
|
|
25
|
-
|
|
20
|
+
- [Vue.js](https://vuejs.org/about/team.html) Core Team
|
|
21
|
+
- [Vue.js Japan User Group](https://github.com/vuejs-jp) Core Staff
|
|
22
|
+
- [Vite+](https://github.com/voidzero-dev/vite-plus) Core Contributor
|
|
23
|
+
- [株式会社メイツ](https://github.com/mates-inc) Chief Engineer
|
|
24
|
+
|
|
25
|
+
## INTERESTS
|
|
26
|
+
|
|
27
|
+
- Web Frontend, Vue, Design, Art, Rust, Performance Tuning
|
|
28
|
+
- Programming Languages, Language Processors, OS and Browser Systems Programming
|
|
29
|
+
- Engineering Philosophy, OSS, Jazz, Twitter
|
|
30
|
+
|
|
31
|
+
## TASK SURFACE
|
|
32
|
+
|
|
33
|
+
Tasks / recent work / PR requests
|
|
34
|
+
|
|
35
|
+
- vault: [taskgraph/public-vault](https://github.com/ubugeeei-taskgraph/public-vault)
|
|
36
|
+
|
|
37
|
+
## CREATOR OF
|
|
38
|
+
|
|
39
|
+
- [chibivue](https://github.com/chibivue-land/chibivue): a minimal Vue.js implementation and online book for learning Vue.js step by step
|
|
40
|
+
- [Vize](https://github.com/ubugeeei/vize): compiler, linter, typechecker, formatter, story system, and lsp for Vue.js in Rust
|
|
41
|
+
- [Ox Content](https://github.com/ubugeeei/ox-content): framework-agnostic documentation tooling with a high-performance Markdown parser in Rust
|
|
42
|
+
- [ubugeeei/start](https://github.com/ubugeeei/start): opinionated defaults and engineering rules for AI-assisted project creation
|
|
43
|
+
- [ubugeeei/style-guide.vue](https://github.com/ubugeeei/style-guide.vue): my personal Vue style guide and engineering preferences
|
|
44
|
+
- [ubugeeei/origin](https://github.com/ubugeeei/origin): an opinionated personal macOS workstation configuration built with Nix, nix-darwin, and Home Manager
|
|
45
|
+
- [ubugeeei-taskgraph](https://github.com/ubugeeei-taskgraph): a task management system with a public vault, shared config, and a PR-based request surface
|
|
46
|
+
- [reading-vuejs-core-vapor](https://github.com/ubugeeei/reading-vuejs-core-vapor): a book for reading and understanding the Vue Vapor implementation
|
|
47
|
+
- [fwgsl](https://github.com/ubugeeei/fwgsl): a pure functional language for WebGPU that compiles to WGSL
|
|
48
|
+
- [relanote](https://github.com/ubugeeei/relanote): a music programming language based on relative intervals
|
|
49
|
+
- [Vapor Moon](https://github.com/ubugeeei/vapor-moon): a MoonBit-first SFC toolchain with Vue-like authoring and direct DOM / SSR output
|
|
50
|
+
- [Mbt on Rails](https://github.com/ubugeeei/mbt-on-rails): a MoonBit-first, Rails-inspired framework skeleton
|
|
51
|
+
- [ush](https://github.com/ubugeeei/ush): an experimental Rust shell that stays POSIX-first and compiles `.ush` to portable `sh`
|
|
52
|
+
|
|
53
|
+
## SELECTED POSTS
|
|
54
|
+
|
|
55
|
+
1. [Characterize Vue.js](https://wtrclred.io/en/posts/07)
|
|
56
|
+
2. [React is React, just.](https://wtrclred.io/en/posts/01)
|
|
57
|
+
3. [React is React, just. Part 2](https://wtrclred.io/en/posts/08)
|
|
58
|
+
4. [What is Vue.js? It's just a language lol](https://wtrclred.io/en/posts/05)
|
|
59
|
+
5. [Vue.js is not Easy. It is Approachable.](https://wtrclred.io/en/posts/06)
|
|
60
|
+
6. [Signals and Signals. And Retained UI.](https://wtrclred.io/en/posts/12)
|
|
61
|
+
|
|
62
|
+
## CONTRIBUTING TO
|
|
63
|
+
|
|
64
|
+
- [Vite+](https://github.com/voidzero-dev/vite-plus)
|
|
65
|
+
- [vite-task](https://github.com/voidzero-dev/vite-task)
|
|
66
|
+
- [oxc](https://github.com/oxc-project/oxc)
|
|
67
|
+
|
|
68
|
+
## SPACES
|
|
69
|
+
|
|
70
|
+
- king: [chibivue.land](https://chibivue.land)
|
|
71
|
+
- blog: [wtrclred.io](https://wtrclred.io)
|
|
72
|
+
|
|
73
|
+
## LINKS
|
|
74
|
+
|
|
75
|
+
- github: [ubugeeei](https://github.com/ubugeeei)
|
|
76
|
+
- twitter: [ubugeeei](https://x.com/ubugeeei)
|
|
77
|
+
- discord: [ubugeeei](https://discord.com/users/ubugeeei)
|
|
78
|
+
- sponsor: [sponsors/ubugeeei](https://github.com/sponsors/ubugeeei)
|
|
79
|
+
|
|
80
|
+
## EX
|
|
81
|
+
|
|
82
|
+
- [vuejs/vue-vapor](https://github.com/vuejs/vue-vapor)
|
|
83
|
+
- [vuejs-jp/vuefes-2025-website](https://github.com/vuejs-jp/vuefes-2025-website)
|
|
84
|
+
|
|
85
|
+
## SUPPORT
|
|
86
|
+
|
|
87
|
+

|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
//#region src/content.ts
|
|
3
|
+
const messages = {
|
|
4
|
+
en: {
|
|
5
|
+
name: "ubugeeei",
|
|
6
|
+
nickname: "もののけ王",
|
|
7
|
+
tagline: "うにをくらえ",
|
|
8
|
+
location: "Tokyo 🇯🇵",
|
|
9
|
+
quote: "発言は全て現代アートだよ( ◠‿◠ )",
|
|
10
|
+
taskSurface: "Tasks / recent work / PR requests",
|
|
11
|
+
uni: "✹ 🦀 🦆 彡..。o",
|
|
12
|
+
interests: [
|
|
13
|
+
"Web Frontend",
|
|
14
|
+
"Vue",
|
|
15
|
+
"Design",
|
|
16
|
+
"Art",
|
|
17
|
+
"Rust",
|
|
18
|
+
"Performance Tuning",
|
|
19
|
+
"Programming Languages",
|
|
20
|
+
"Language Processors",
|
|
21
|
+
"OS and Browser Systems Programming",
|
|
22
|
+
"Engineering Philosophy",
|
|
23
|
+
"OSS",
|
|
24
|
+
"Jazz",
|
|
25
|
+
"Twitter"
|
|
26
|
+
],
|
|
27
|
+
creator: "Creator of",
|
|
28
|
+
contribute: "Contributing to",
|
|
29
|
+
selectedPosts: "Selected Posts"
|
|
30
|
+
},
|
|
31
|
+
ja: {
|
|
32
|
+
name: "ubugeeei",
|
|
33
|
+
nickname: "もののけ王",
|
|
34
|
+
tagline: "うにをくらえ",
|
|
35
|
+
location: "Tokyo 🇯🇵",
|
|
36
|
+
quote: "発言は全て現代アートだよ( ◠‿◠ )",
|
|
37
|
+
taskSurface: "Tasks / recent work / PR requests",
|
|
38
|
+
uni: "✹ 🦀 🦆 彡..。o",
|
|
39
|
+
interests: [
|
|
40
|
+
"Web Frontend",
|
|
41
|
+
"Vue",
|
|
42
|
+
"Design",
|
|
43
|
+
"Art",
|
|
44
|
+
"Rust",
|
|
45
|
+
"Performance Tuning",
|
|
46
|
+
"Programming Languages",
|
|
47
|
+
"Language Processors",
|
|
48
|
+
"OS and Browser Systems Programming",
|
|
49
|
+
"Engineering Philosophy",
|
|
50
|
+
"OSS",
|
|
51
|
+
"Jazz",
|
|
52
|
+
"Twitter"
|
|
53
|
+
],
|
|
54
|
+
creator: "Creator of",
|
|
55
|
+
contribute: "Contributing to",
|
|
56
|
+
selectedPosts: "Selected Posts"
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
function detectLocale() {
|
|
60
|
+
const args = process.argv.slice(2);
|
|
61
|
+
if (args.includes("--ja") || args.includes("-j") || args.includes("--japanese")) return "ja";
|
|
62
|
+
if (args.includes("--en") || args.includes("-e") || args.includes("--english")) return "en";
|
|
63
|
+
if ((process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || "").toLowerCase().startsWith("ja")) return "ja";
|
|
64
|
+
return "en";
|
|
65
|
+
}
|
|
66
|
+
const projects = [
|
|
67
|
+
{
|
|
68
|
+
name: "chibivue",
|
|
69
|
+
url: "https://github.com/chibivue-land/chibivue",
|
|
70
|
+
description: "A minimal Vue.js implementation and online book for learning Vue.js step by step."
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: "Vize",
|
|
74
|
+
url: "https://github.com/ubugeeei/vize",
|
|
75
|
+
description: "Compiler, linter, typechecker, formatter, story system, and lsp for Vue.js in Rust."
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "Ox Content",
|
|
79
|
+
url: "https://github.com/ubugeeei/ox-content",
|
|
80
|
+
description: "Framework-agnostic documentation tooling with a high-performance Markdown parser in Rust."
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "ubugeeei/start",
|
|
84
|
+
url: "https://github.com/ubugeeei/start",
|
|
85
|
+
description: "Opinionated defaults and engineering rules for AI-assisted project creation."
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "ubugeeei/style-guide.vue",
|
|
89
|
+
url: "https://github.com/ubugeeei/style-guide.vue",
|
|
90
|
+
description: "My personal Vue style guide and engineering preferences."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "ubugeeei/origin",
|
|
94
|
+
url: "https://github.com/ubugeeei/origin",
|
|
95
|
+
description: "An opinionated personal macOS workstation configuration built with Nix, nix-darwin, and Home Manager."
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "ubugeeei-taskgraph",
|
|
99
|
+
url: "https://github.com/ubugeeei-taskgraph",
|
|
100
|
+
description: "A task management system with a public vault, shared config, and a PR-based request surface."
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "reading-vuejs-core-vapor",
|
|
104
|
+
url: "https://github.com/ubugeeei/reading-vuejs-core-vapor",
|
|
105
|
+
description: "A book for reading and understanding the Vue Vapor implementation."
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "fwgsl",
|
|
109
|
+
url: "https://github.com/ubugeeei/fwgsl",
|
|
110
|
+
description: "A pure functional language for WebGPU that compiles to WGSL."
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: "relanote",
|
|
114
|
+
url: "https://github.com/ubugeeei/relanote",
|
|
115
|
+
description: "A music programming language based on relative intervals."
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "Vapor Moon",
|
|
119
|
+
url: "https://github.com/ubugeeei/vapor-moon",
|
|
120
|
+
description: "A MoonBit-first SFC toolchain with Vue-like authoring and direct DOM / SSR output."
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "Mbt on Rails",
|
|
124
|
+
url: "https://github.com/ubugeeei/mbt-on-rails",
|
|
125
|
+
description: "A MoonBit-first, Rails-inspired framework skeleton."
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "ush",
|
|
129
|
+
url: "https://github.com/ubugeeei/ush",
|
|
130
|
+
description: "An experimental Rust shell that stays POSIX-first and compiles .ush to portable sh."
|
|
131
|
+
}
|
|
132
|
+
];
|
|
133
|
+
const blogPosts = [
|
|
134
|
+
{
|
|
135
|
+
title: "Characterize Vue.js",
|
|
136
|
+
url: "https://wtrclred.io/en/posts/07"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
title: "React is React, just.",
|
|
140
|
+
url: "https://wtrclred.io/en/posts/01"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
title: "React is React, just. Part 2",
|
|
144
|
+
url: "https://wtrclred.io/en/posts/08"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
title: "What is Vue.js? It's just a language lol",
|
|
148
|
+
url: "https://wtrclred.io/en/posts/05"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
title: "Vue.js is not Easy. It is Approachable.",
|
|
152
|
+
url: "https://wtrclred.io/en/posts/06"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
title: "Signals and Signals. And Retained UI.",
|
|
156
|
+
url: "https://wtrclred.io/en/posts/12"
|
|
157
|
+
}
|
|
158
|
+
];
|
|
159
|
+
const contributes = [
|
|
160
|
+
{
|
|
161
|
+
name: "Vite+",
|
|
162
|
+
url: "https://github.com/voidzero-dev/vite-plus"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "vite-task",
|
|
166
|
+
url: "https://github.com/voidzero-dev/vite-task"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
name: "oxc",
|
|
170
|
+
url: "https://github.com/oxc-project/oxc"
|
|
171
|
+
}
|
|
172
|
+
];
|
|
173
|
+
const OSC = "\x1B]";
|
|
174
|
+
const BEL = "\x07";
|
|
175
|
+
const rgb = (r, g, b) => `[38;2;${r};${g};${b}m`;
|
|
176
|
+
rgb(66, 184, 131), rgb(52, 152, 108), rgb(100, 207, 159), rgb(0, 200, 220), rgb(255, 160, 70), rgb(255, 100, 150), rgb(180, 120, 255), rgb(255, 215, 0), rgb(255, 255, 255), rgb(150, 150, 150), rgb(80, 80, 80), rgb(66, 184, 131);
|
|
177
|
+
rgb(66, 184, 131), rgb(60, 170, 140), rgb(50, 160, 160), rgb(40, 150, 180), rgb(30, 140, 200), rgb(20, 130, 220), rgb(80, 120, 240), rgb(140, 110, 255);
|
|
178
|
+
function link(url, text) {
|
|
179
|
+
return `${OSC}8;;${url}${BEL}${text}${OSC}8;;${BEL}`;
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/layout.ts
|
|
183
|
+
const ESC_CHAR = String.fromCharCode(27);
|
|
184
|
+
const BEL_CHAR = String.fromCharCode(7);
|
|
185
|
+
`${ESC_CHAR}`, `${BEL_CHAR}`;
|
|
186
|
+
const ansiPattern = new RegExp(`${ESC_CHAR}\\[[0-9;]*m|${ESC_CHAR}\\]8;;[^${BEL_CHAR}]*${BEL_CHAR}`, "g");
|
|
187
|
+
const graphemeSegmenter = new Intl.Segmenter("en", { granularity: "grapheme" });
|
|
188
|
+
function isZeroWidthCodePoint(code) {
|
|
189
|
+
return code === 8205 || code >= 768 && code <= 879 || code >= 65024 && code <= 65039 || code >= 917760 && code <= 917999;
|
|
190
|
+
}
|
|
191
|
+
function isRegionalIndicator(code) {
|
|
192
|
+
return code >= 127462 && code <= 127487;
|
|
193
|
+
}
|
|
194
|
+
function isEmojiCodePoint(code) {
|
|
195
|
+
return code >= 126976 && code <= 129791 || code >= 9728 && code <= 9983 || code >= 9984 && code <= 10175;
|
|
196
|
+
}
|
|
197
|
+
function isWideCodePoint(code) {
|
|
198
|
+
return code >= 4352 && code <= 4447 || code >= 11904 && code <= 40959 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65055 || code >= 65072 && code <= 65135 || code >= 65280 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 131072 && code <= 196607;
|
|
199
|
+
}
|
|
200
|
+
function isTerminalWideSymbol(code) {
|
|
201
|
+
return code === 8226 || code === 8255 || code === 9696 || code === 10041;
|
|
202
|
+
}
|
|
203
|
+
function getGraphemeWidth(segment) {
|
|
204
|
+
const codePoints = Array.from(segment, (char) => char.codePointAt(0) || 0);
|
|
205
|
+
const visible = codePoints.filter((code) => !isZeroWidthCodePoint(code));
|
|
206
|
+
if (visible.length === 0) return 0;
|
|
207
|
+
if (visible.every(isRegionalIndicator)) return 4;
|
|
208
|
+
if (codePoints.some(isEmojiCodePoint)) return 2;
|
|
209
|
+
if (visible.some(isWideCodePoint)) return 2;
|
|
210
|
+
if (visible.some(isTerminalWideSymbol)) return 2;
|
|
211
|
+
return 1;
|
|
212
|
+
}
|
|
213
|
+
function pushTextTokens(tokens, text, widthFn = getGraphemeWidth) {
|
|
214
|
+
for (const { segment } of graphemeSegmenter.segment(text)) tokens.push({
|
|
215
|
+
type: "grapheme",
|
|
216
|
+
value: segment,
|
|
217
|
+
width: widthFn(segment)
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
function tokenizeDisplayString(str, widthFn = getGraphemeWidth) {
|
|
221
|
+
const tokens = [];
|
|
222
|
+
let lastIndex = 0;
|
|
223
|
+
for (const match of str.matchAll(ansiPattern)) {
|
|
224
|
+
const index = match.index ?? 0;
|
|
225
|
+
if (index > lastIndex) pushTextTokens(tokens, str.slice(lastIndex, index), widthFn);
|
|
226
|
+
tokens.push({
|
|
227
|
+
type: "ansi",
|
|
228
|
+
value: match[0]
|
|
229
|
+
});
|
|
230
|
+
lastIndex = index + match[0].length;
|
|
231
|
+
}
|
|
232
|
+
if (lastIndex < str.length) pushTextTokens(tokens, str.slice(lastIndex), widthFn);
|
|
233
|
+
return tokens;
|
|
234
|
+
}
|
|
235
|
+
function getDisplayWidthWith(str, widthFn = getGraphemeWidth) {
|
|
236
|
+
let width = 0;
|
|
237
|
+
for (const token of tokenizeDisplayString(str, widthFn)) if (token.type === "grapheme") width += token.width;
|
|
238
|
+
return width;
|
|
239
|
+
}
|
|
240
|
+
function getDisplayWidth(str) {
|
|
241
|
+
return getDisplayWidthWith(str);
|
|
242
|
+
}
|
|
243
|
+
function clamp(value, min, max) {
|
|
244
|
+
return Math.max(min, Math.min(max, value));
|
|
245
|
+
}
|
|
246
|
+
const INNER_WIDTH = clamp((process.stdout.columns ?? 92) - 2, 78, 100) - 2 - 1 - 2;
|
|
247
|
+
const RIGHT_COLUMN_WIDTH = clamp(Math.floor(INNER_WIDTH * .38), 32, 38);
|
|
248
|
+
INNER_WIDTH - 3 - RIGHT_COLUMN_WIDTH;
|
|
249
|
+
function wrapText(text, maxWidth) {
|
|
250
|
+
return wrapTextWith(text, maxWidth);
|
|
251
|
+
}
|
|
252
|
+
function wrapTextWith(text, maxWidth, widthFn = getGraphemeWidth) {
|
|
253
|
+
const words = text.split(" ");
|
|
254
|
+
const lines = [];
|
|
255
|
+
let current = "";
|
|
256
|
+
for (const word of words) {
|
|
257
|
+
const candidate = current ? `${current} ${word}` : word;
|
|
258
|
+
if (getDisplayWidthWith(candidate, widthFn) <= maxWidth) {
|
|
259
|
+
current = candidate;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (current) lines.push(current);
|
|
263
|
+
if (getDisplayWidthWith(word, widthFn) <= maxWidth) {
|
|
264
|
+
current = word;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
let chunk = "";
|
|
268
|
+
for (const char of word) {
|
|
269
|
+
const chunkCandidate = chunk + char;
|
|
270
|
+
if (getDisplayWidthWith(chunkCandidate, widthFn) <= maxWidth) {
|
|
271
|
+
chunk = chunkCandidate;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (chunk) lines.push(chunk);
|
|
275
|
+
chunk = char;
|
|
276
|
+
}
|
|
277
|
+
current = chunk;
|
|
278
|
+
}
|
|
279
|
+
if (current) lines.push(current);
|
|
280
|
+
return lines;
|
|
281
|
+
}
|
|
282
|
+
function wrapItems(items, maxWidth, separator = " • ") {
|
|
283
|
+
const lines = [];
|
|
284
|
+
let current = "";
|
|
285
|
+
for (const item of items) {
|
|
286
|
+
const candidate = current ? `${current}${separator}${item}` : item;
|
|
287
|
+
if (getDisplayWidth(candidate) <= maxWidth) {
|
|
288
|
+
current = candidate;
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (current) lines.push(current);
|
|
292
|
+
current = item;
|
|
293
|
+
}
|
|
294
|
+
if (current) lines.push(current);
|
|
295
|
+
return lines;
|
|
296
|
+
}
|
|
297
|
+
//#endregion
|
|
298
|
+
//#region src/render.ts
|
|
299
|
+
const PAGE_WIDTH = Math.min(process.stdout.columns ?? 80, 80);
|
|
300
|
+
const INDENT = " ";
|
|
301
|
+
const BODY_WIDTH = Math.max(56, PAGE_WIDTH - getDisplayWidth(INDENT));
|
|
302
|
+
const MARK = "UBUGEEEI(1)";
|
|
303
|
+
const spaces = [{
|
|
304
|
+
label: "king",
|
|
305
|
+
text: "chibivue.land",
|
|
306
|
+
url: "https://chibivue.land"
|
|
307
|
+
}, {
|
|
308
|
+
label: "blog",
|
|
309
|
+
text: "wtrclred.io",
|
|
310
|
+
url: "https://wtrclred.io"
|
|
311
|
+
}];
|
|
312
|
+
const links = [
|
|
313
|
+
{
|
|
314
|
+
label: "github",
|
|
315
|
+
text: "ubugeeei",
|
|
316
|
+
url: "https://github.com/ubugeeei"
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
label: "twitter",
|
|
320
|
+
text: "ubugeeei",
|
|
321
|
+
url: "https://x.com/ubugeeei"
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
label: "discord",
|
|
325
|
+
text: "ubugeeei",
|
|
326
|
+
url: "https://discord.com/users/ubugeeei"
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
label: "sponsor",
|
|
330
|
+
text: "sponsors/ubugeeei",
|
|
331
|
+
url: "https://github.com/sponsors/ubugeeei"
|
|
332
|
+
}
|
|
333
|
+
];
|
|
334
|
+
const examples = [{
|
|
335
|
+
text: "vuejs/vue-vapor",
|
|
336
|
+
url: "https://github.com/vuejs/vue-vapor"
|
|
337
|
+
}, {
|
|
338
|
+
text: "vuejs-jp/vuefes-2025-website",
|
|
339
|
+
url: "https://github.com/vuejs-jp/vuefes-2025-website"
|
|
340
|
+
}];
|
|
341
|
+
function formatHeader() {
|
|
342
|
+
const gap = Math.max(2, PAGE_WIDTH - getDisplayWidth(MARK) * 2);
|
|
343
|
+
return `${MARK}${" ".repeat(gap)}${MARK}`;
|
|
344
|
+
}
|
|
345
|
+
function formatSection(title, lines) {
|
|
346
|
+
return [
|
|
347
|
+
title,
|
|
348
|
+
...lines,
|
|
349
|
+
""
|
|
350
|
+
];
|
|
351
|
+
}
|
|
352
|
+
function wrapIndented(text, indent = INDENT, width = BODY_WIDTH) {
|
|
353
|
+
return wrapText(text, width).map((line) => `${indent}${line}`);
|
|
354
|
+
}
|
|
355
|
+
function formatProject(project) {
|
|
356
|
+
return [`${INDENT}${link(project.url, project.name)}`, ...wrapIndented(project.description, `${INDENT}${INDENT}`)];
|
|
357
|
+
}
|
|
358
|
+
function formatPost(post, index) {
|
|
359
|
+
const lead = `${index + 1}. `;
|
|
360
|
+
return wrapText(post.title, BODY_WIDTH - getDisplayWidth(lead)).map((line, lineIndex) => lineIndex === 0 ? `${INDENT}${lead}${link(post.url, line)}` : `${INDENT}${" ".repeat(getDisplayWidth(lead))}${link(post.url, line)}`);
|
|
361
|
+
}
|
|
362
|
+
function formatContribution(item) {
|
|
363
|
+
return [`${INDENT}${link(item.url, item.name)}`];
|
|
364
|
+
}
|
|
365
|
+
function formatLabeledLink(label, text, url) {
|
|
366
|
+
return [`${INDENT}${label.padEnd(10)}${link(url, text)}`];
|
|
367
|
+
}
|
|
368
|
+
function renderProfile(locale) {
|
|
369
|
+
const t = messages[locale];
|
|
370
|
+
const roleLines = [
|
|
371
|
+
`${INDENT}${link("https://vuejs.org/about/team.html", "Vue.js")} Core Team`,
|
|
372
|
+
`${INDENT}${link("https://github.com/vuejs-jp", "Vue.js Japan User Group")} Core Staff`,
|
|
373
|
+
`${INDENT}${link("https://github.com/voidzero-dev/vite-plus", "Vite+")} Core Contributor`,
|
|
374
|
+
`${INDENT}${link("https://github.com/mates-inc", "株式会社メイツ")} Chief Engineer`
|
|
375
|
+
];
|
|
376
|
+
const interestLines = wrapItems(t.interests, BODY_WIDTH, ", ").map((line) => `${INDENT}${line}`);
|
|
377
|
+
return [
|
|
378
|
+
formatHeader(),
|
|
379
|
+
"",
|
|
380
|
+
...formatSection("NAME", [`${INDENT}ubugeeei - software engineer`]),
|
|
381
|
+
...formatSection("SYNOPSIS", [`${INDENT}npx ubugeeei`]),
|
|
382
|
+
...formatSection("PROFILE", [
|
|
383
|
+
`${INDENT}${t.nickname}`,
|
|
384
|
+
`${INDENT}${t.tagline} ${t.uni}`,
|
|
385
|
+
`${INDENT}${t.location}`,
|
|
386
|
+
`${INDENT}${t.quote}`
|
|
387
|
+
]),
|
|
388
|
+
...formatSection("ROLES", roleLines),
|
|
389
|
+
...formatSection("INTERESTS", interestLines),
|
|
390
|
+
...formatSection("TASK SURFACE", [...wrapIndented(t.taskSurface), ...formatLabeledLink("vault", "taskgraph/public-vault", "https://github.com/ubugeeei-taskgraph/public-vault")]),
|
|
391
|
+
...formatSection("CREATOR OF", projects.flatMap(formatProject)),
|
|
392
|
+
...formatSection("SELECTED POSTS", blogPosts.flatMap(formatPost)),
|
|
393
|
+
...formatSection("CONTRIBUTING TO", contributes.flatMap(formatContribution)),
|
|
394
|
+
...formatSection("SPACES", spaces.flatMap((item) => formatLabeledLink(item.label, item.text, item.url))),
|
|
395
|
+
...formatSection("LINKS", links.flatMap((item) => formatLabeledLink(item.label, item.text, item.url))),
|
|
396
|
+
...formatSection("EX", examples.map((item) => `${INDENT}${link(item.url, item.text)}`)),
|
|
397
|
+
formatHeader(),
|
|
398
|
+
""
|
|
399
|
+
].join("\n");
|
|
400
|
+
}
|
|
401
|
+
//#endregion
|
|
402
|
+
//#region src/cli.ts
|
|
403
|
+
console.log(renderProfile(detectLocale()));
|
|
404
|
+
//#endregion
|
|
405
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,47 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ubugeeei",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Hi! I'm ubugeeei, Vue.js team member, author of chibivue and vize",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "index.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"ubugeeei": "dist/cli.js"
|
|
9
|
-
},
|
|
10
|
-
"exports": {
|
|
11
|
-
".": "./index.js"
|
|
12
|
-
},
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "bun run typecheck && bun build bin/cli.ts --outdir dist --target node",
|
|
15
|
-
"dev": "bun run bin/cli.ts",
|
|
16
|
-
"typecheck": "tsc --noEmit"
|
|
17
|
-
},
|
|
18
5
|
"keywords": [
|
|
19
|
-
"cli",
|
|
20
6
|
"card",
|
|
7
|
+
"chibivue",
|
|
8
|
+
"cli",
|
|
21
9
|
"npx",
|
|
22
|
-
"vue"
|
|
23
|
-
"chibivue"
|
|
10
|
+
"vue"
|
|
24
11
|
],
|
|
25
|
-
"author": "ubugeeei",
|
|
26
|
-
"license": "MIT",
|
|
27
12
|
"homepage": "https://wtrclred.io",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/ubugeeei/ubugeeei/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "ubugeeei",
|
|
28
18
|
"repository": {
|
|
29
19
|
"type": "git",
|
|
30
20
|
"url": "git+https://github.com/ubugeeei/ubugeeei.git"
|
|
31
21
|
},
|
|
32
|
-
"bugs": {
|
|
33
|
-
"url": "https://github.com/ubugeeei/ubugeeei/issues"
|
|
34
|
-
},
|
|
35
22
|
"funding": "https://github.com/sponsors/ubugeeei",
|
|
36
|
-
"
|
|
37
|
-
"
|
|
23
|
+
"bin": {
|
|
24
|
+
"ubugeeei": "dist/cli.mjs"
|
|
38
25
|
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"type": "module",
|
|
39
30
|
"devDependencies": {
|
|
40
|
-
"@types/
|
|
41
|
-
"typescript": "^5.0.0"
|
|
31
|
+
"@types/node": "^24.0.0",
|
|
32
|
+
"typescript": "^5.0.0",
|
|
33
|
+
"vite-plus": "^0.1.14"
|
|
42
34
|
},
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=24.0.0"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"dev": "node dist/cli.mjs",
|
|
40
|
+
"watch": "vp pack --watch"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/assets/cover.png
DELETED
|
Binary file
|
package/dist/cli.js
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// bin/cli.ts
|
|
4
|
-
var ESC = "\x1B";
|
|
5
|
-
var OSC = "\x1B]";
|
|
6
|
-
var BEL = "\x07";
|
|
7
|
-
var RESET = `${ESC}[0m`;
|
|
8
|
-
var BOLD = `${ESC}[1m`;
|
|
9
|
-
var DIM = `${ESC}[2m`;
|
|
10
|
-
var ITALIC = `${ESC}[3m`;
|
|
11
|
-
var rgb = (r, g, b) => `${ESC}[38;2;${r};${g};${b}m`;
|
|
12
|
-
var COLORS = {
|
|
13
|
-
vue: rgb(66, 184, 131),
|
|
14
|
-
vueDark: rgb(52, 152, 108),
|
|
15
|
-
vueLight: rgb(100, 207, 159),
|
|
16
|
-
cyan: rgb(0, 200, 220),
|
|
17
|
-
orange: rgb(255, 160, 70),
|
|
18
|
-
pink: rgb(255, 100, 150),
|
|
19
|
-
purple: rgb(180, 120, 255),
|
|
20
|
-
yellow: rgb(255, 215, 0),
|
|
21
|
-
white: rgb(255, 255, 255),
|
|
22
|
-
gray: rgb(150, 150, 150),
|
|
23
|
-
darkGray: rgb(80, 80, 80),
|
|
24
|
-
border: rgb(66, 184, 131)
|
|
25
|
-
};
|
|
26
|
-
var GRADIENT = [
|
|
27
|
-
rgb(66, 184, 131),
|
|
28
|
-
rgb(60, 170, 140),
|
|
29
|
-
rgb(50, 160, 160),
|
|
30
|
-
rgb(40, 150, 180),
|
|
31
|
-
rgb(30, 140, 200),
|
|
32
|
-
rgb(20, 130, 220),
|
|
33
|
-
rgb(80, 120, 240),
|
|
34
|
-
rgb(140, 110, 255)
|
|
35
|
-
];
|
|
36
|
-
function gradientText(text) {
|
|
37
|
-
let result = "";
|
|
38
|
-
for (let i = 0;i < text.length; i++) {
|
|
39
|
-
const colorIndex = Math.floor(i / text.length * GRADIENT.length);
|
|
40
|
-
result += GRADIENT[colorIndex] + text[i];
|
|
41
|
-
}
|
|
42
|
-
return result + RESET;
|
|
43
|
-
}
|
|
44
|
-
var link = (url, text) => `${OSC}8;;${url}${BEL}${text}${OSC}8;;${BEL}`;
|
|
45
|
-
var messages = {
|
|
46
|
-
en: {
|
|
47
|
-
name: "ubugeeei",
|
|
48
|
-
nickname: "もののけ王",
|
|
49
|
-
tagline: "うにをくらえ",
|
|
50
|
-
uni: "\uD83E\uDD80彡..。o",
|
|
51
|
-
interests: "Jazz, Art, Compiler, Vue",
|
|
52
|
-
memberOf: "Member of",
|
|
53
|
-
author: "Author of",
|
|
54
|
-
kingOf: "King of",
|
|
55
|
-
chiefEngineerOf: "Chief Engineer of",
|
|
56
|
-
contribute: "Contribute",
|
|
57
|
-
blog: "Blog"
|
|
58
|
-
},
|
|
59
|
-
ja: {
|
|
60
|
-
name: "ubugeeei",
|
|
61
|
-
nickname: "もののけ王",
|
|
62
|
-
tagline: "うにをくらえ",
|
|
63
|
-
uni: "\uD83E\uDD80彡..。o",
|
|
64
|
-
interests: "Jazz, Art, Compiler, Vue",
|
|
65
|
-
memberOf: "Member of",
|
|
66
|
-
author: "Author of",
|
|
67
|
-
kingOf: "King of",
|
|
68
|
-
chiefEngineerOf: "Chief Engineer of",
|
|
69
|
-
contribute: "Contribute",
|
|
70
|
-
blog: "Blog"
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
function detectLocale() {
|
|
74
|
-
const args = process.argv.slice(2);
|
|
75
|
-
if (args.includes("--ja") || args.includes("-j") || args.includes("--japanese"))
|
|
76
|
-
return "ja";
|
|
77
|
-
if (args.includes("--en") || args.includes("-e") || args.includes("--english"))
|
|
78
|
-
return "en";
|
|
79
|
-
if ((process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || "").toLowerCase().startsWith("ja"))
|
|
80
|
-
return "ja";
|
|
81
|
-
return "en";
|
|
82
|
-
}
|
|
83
|
-
var projects = [
|
|
84
|
-
{ name: "chibivue", url: "https://github.com/chibivue-land/chibivue" },
|
|
85
|
-
{
|
|
86
|
-
name: "reading-vuejs-core-vapor",
|
|
87
|
-
url: "https://github.com/ubugeeei/reading-vuejs-core-vapor"
|
|
88
|
-
},
|
|
89
|
-
{ name: "vize", url: "https://github.com/ubugeeei/vize" },
|
|
90
|
-
{ name: "ox-content", url: "https://github.com/ubugeeei/ox-content" },
|
|
91
|
-
{ name: "relanote", url: "https://github.com/ubugeeei/relanote" },
|
|
92
|
-
{ name: "learn.nuxt.com (ja)", url: "https://github.com/nuxt/learn.nuxt.com" },
|
|
93
|
-
{
|
|
94
|
-
name: "jp-vue-companies",
|
|
95
|
-
url: "https://github.com/chibivue-land/japanese-companies-using-vuejs"
|
|
96
|
-
}
|
|
97
|
-
];
|
|
98
|
-
var contributes = [
|
|
99
|
-
{ name: "vuejs/core (mainly vapor)", url: "https://github.com/vuejs/core" },
|
|
100
|
-
{ name: "oxc", url: "https://github.com/oxc-project/oxc" },
|
|
101
|
-
{ name: "vuefes", url: "https://github.com/vuejs-jp/vuefes-2025" }
|
|
102
|
-
];
|
|
103
|
-
function getDisplayWidth(str) {
|
|
104
|
-
const clean = str.replace(/\x1b\[[0-9;]*m|\x1b\]8;;[^\x07]*\x07/g, "");
|
|
105
|
-
let width = 0;
|
|
106
|
-
for (const char of clean) {
|
|
107
|
-
const code = char.codePointAt(0) || 0;
|
|
108
|
-
if (code >= 4352 && code <= 4447 || code >= 11904 && code <= 40959 || code >= 44032 && code <= 55215 || code >= 63744 && code <= 64255 || code >= 65040 && code <= 65055 || code >= 65072 && code <= 65135 || code >= 65280 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744 && code <= 129535 || code >= 131072 && code <= 196607)
|
|
109
|
-
width += 2;
|
|
110
|
-
else
|
|
111
|
-
width += 1;
|
|
112
|
-
}
|
|
113
|
-
return width;
|
|
114
|
-
}
|
|
115
|
-
function padRight(str, targetWidth) {
|
|
116
|
-
const padding = targetWidth - getDisplayWidth(str);
|
|
117
|
-
return str + " ".repeat(Math.max(0, padding));
|
|
118
|
-
}
|
|
119
|
-
var WIDTH = 62;
|
|
120
|
-
var INNER_WIDTH = WIDTH - 4;
|
|
121
|
-
function createLine(content) {
|
|
122
|
-
return `${COLORS.border}│${RESET} ${padRight(content, INNER_WIDTH)} ${COLORS.border}│${RESET}`;
|
|
123
|
-
}
|
|
124
|
-
function createEmptyLine() {
|
|
125
|
-
return `${COLORS.border}│${RESET}${" ".repeat(WIDTH - 2)}${COLORS.border}│${RESET}`;
|
|
126
|
-
}
|
|
127
|
-
function createSeparator() {
|
|
128
|
-
return `${COLORS.border}├${"─".repeat(WIDTH - 2)}┤${RESET}`;
|
|
129
|
-
}
|
|
130
|
-
function main() {
|
|
131
|
-
const t = messages[detectLocale()];
|
|
132
|
-
const topBorder = `${COLORS.border}╭${"─".repeat(WIDTH - 2)}╮${RESET}`;
|
|
133
|
-
const bottomBorder = `${COLORS.border}╰${"─".repeat(WIDTH - 2)}╯${RESET}`;
|
|
134
|
-
const projectLinks = projects.map((p) => link(p.url, COLORS.cyan + p.name + RESET));
|
|
135
|
-
const contributeLinks = contributes.map((p) => link(p.url, COLORS.purple + p.name + RESET));
|
|
136
|
-
const lines = [
|
|
137
|
-
"",
|
|
138
|
-
topBorder,
|
|
139
|
-
createEmptyLine(),
|
|
140
|
-
createLine(`${BOLD}${gradientText(t.name)}${RESET} ${COLORS.gray}${t.nickname}${RESET}`),
|
|
141
|
-
createLine(`${COLORS.orange}${t.tagline}${RESET} ${t.uni}`),
|
|
142
|
-
createLine(`${DIM}${COLORS.gray}${t.interests}${RESET}`),
|
|
143
|
-
createEmptyLine(),
|
|
144
|
-
createSeparator(),
|
|
145
|
-
createEmptyLine(),
|
|
146
|
-
createLine(`${COLORS.yellow}★${RESET} ${COLORS.white}${t.memberOf}${RESET}`),
|
|
147
|
-
createLine(` ${link("https://vuejs.org", COLORS.vue + BOLD + "Vue.js" + RESET)} ${link("https://vuejs-jp.org", COLORS.vue + "Vue.js Japan" + RESET)}`),
|
|
148
|
-
createEmptyLine(),
|
|
149
|
-
createLine(`${COLORS.pink}♛${RESET} ${COLORS.white}${t.kingOf}${RESET}`),
|
|
150
|
-
createLine(` ${link("https://chibivue.land", COLORS.cyan + "chibivue.land" + RESET)}`),
|
|
151
|
-
createEmptyLine(),
|
|
152
|
-
createLine(`${COLORS.orange}⚙${RESET} ${COLORS.white}${t.chiefEngineerOf}${RESET}`),
|
|
153
|
-
createLine(` ${link("https://github.com/mates-system", COLORS.cyan + "@mates-system" + RESET)}`),
|
|
154
|
-
createEmptyLine(),
|
|
155
|
-
createSeparator(),
|
|
156
|
-
createEmptyLine(),
|
|
157
|
-
createLine(`${COLORS.vue}✦${RESET} ${COLORS.white}${t.author}${RESET}`),
|
|
158
|
-
createLine(` ${projectLinks.slice(0, 2).join(", ")}`),
|
|
159
|
-
createLine(` ${projectLinks.slice(2, 4).join(", ")}`),
|
|
160
|
-
createLine(` ${projectLinks.slice(4, 6).join(", ")}`),
|
|
161
|
-
createLine(` ${projectLinks.slice(6).join(", ")}`),
|
|
162
|
-
createEmptyLine(),
|
|
163
|
-
createLine(`${COLORS.purple}◈${RESET} ${COLORS.white}${t.contribute}${RESET}`),
|
|
164
|
-
createLine(` ${contributeLinks.join(", ")}`),
|
|
165
|
-
createEmptyLine(),
|
|
166
|
-
createLine(`${COLORS.cyan}✎${RESET} ${COLORS.white}${t.blog}${RESET}`),
|
|
167
|
-
createLine(` ${link("https://wtrclred.io", COLORS.cyan + "wtrclred.io" + RESET)}`),
|
|
168
|
-
createEmptyLine(),
|
|
169
|
-
createSeparator(),
|
|
170
|
-
createEmptyLine(),
|
|
171
|
-
createLine(`${COLORS.white}GitHub${RESET} ${link("https://github.com/ubugeeei", COLORS.gray + "github.com/ubugeeei" + RESET)}`),
|
|
172
|
-
createLine(`${COLORS.white}Twitter${RESET} ${link("https://x.com/ubugeeei", COLORS.gray + "@ubugeeei" + RESET)}`),
|
|
173
|
-
createLine(`${COLORS.white}Discord${RESET} ${link("https://discord.com/users/ubugeeei", COLORS.gray + "ubugeeei" + RESET)}`),
|
|
174
|
-
createLine(`${COLORS.pink}Sponsor${RESET} ${link("https://github.com/sponsors/ubugeeei", COLORS.pink + "github.com/sponsors/ubugeeei" + RESET)}`),
|
|
175
|
-
createEmptyLine(),
|
|
176
|
-
bottomBorder,
|
|
177
|
-
""
|
|
178
|
-
];
|
|
179
|
-
console.log(lines.join(`
|
|
180
|
-
`));
|
|
181
|
-
}
|
|
182
|
-
main();
|
package/index.js
DELETED