@whykusanagi/corrupted-theme 0.1.7 → 0.1.9
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/CHANGELOG.md +28 -0
- package/README.md +140 -4
- package/docs/COMPONENTS_REFERENCE.md +205 -2
- package/docs/governance/VERSION_MANAGEMENT.md +2 -2
- package/docs/governance/VERSION_REFERENCES.md +65 -202
- package/docs/platforms/NPM_PACKAGE.md +8 -6
- package/examples/advanced/glsl-vortex.html +297 -0
- package/examples/advanced/particles-bg.html +263 -0
- package/examples/basic/corrupted-text.html +2 -3
- package/examples/basic/typing-animation.html +136 -55
- package/examples/button.html +1 -2
- package/examples/card.html +1 -2
- package/examples/extensions-showcase.html +36 -1
- package/examples/form.html +1 -2
- package/examples/index.html +28 -4
- package/examples/layout.html +1 -2
- package/examples/nikke-team-builder.html +1 -2
- package/examples/showcase-complete.html +2 -3
- package/examples/showcase.html +1 -2
- package/package.json +4 -3
- package/src/core/typing-animation.js +274 -106
- package/src/css/theme.css +0 -15
- package/src/lib/corrupted-particles.js +309 -0
- package/src/lib/corrupted-text.js +127 -36
- package/src/lib/corrupted-vortex.js +329 -0
- package/docs/ROADMAP.md +0 -266
- package/examples/advanced/nsfw-corruption.html +0 -348
- package/src/core/corrupted-text.js +0 -300
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
// src/lib/corrupted-vortex.js
|
|
2
|
+
// CorruptedVortex — WebGL1 raymarched spiral-vortex component
|
|
3
|
+
// Part of @whykusanagi/corrupted-theme
|
|
4
|
+
|
|
5
|
+
const VERT_SRC = `
|
|
6
|
+
attribute vec2 aPosition;
|
|
7
|
+
void main() {
|
|
8
|
+
gl_Position = vec4(aPosition, 0.0, 1.0);
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const FRAG_SRC = `
|
|
13
|
+
precision highp float;
|
|
14
|
+
|
|
15
|
+
uniform vec2 uResolution;
|
|
16
|
+
uniform float uTime;
|
|
17
|
+
uniform float uIntensity;
|
|
18
|
+
uniform float uRotationRate;
|
|
19
|
+
uniform float uHue;
|
|
20
|
+
|
|
21
|
+
vec3 hsv(float h, float s, float v) {
|
|
22
|
+
vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
|
|
23
|
+
vec3 p = abs(fract(vec3(h) + K.xyz) * 6.0 - K.www);
|
|
24
|
+
return v * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), s);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
mat2 rotate2D(float a) {
|
|
28
|
+
float c = cos(a), s = sin(a);
|
|
29
|
+
return mat2(c, -s, s, c);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Continuous 3-D accretion disk viewed at ~60° inclination, major axis 45° CW.
|
|
33
|
+
// Perpendicular-distance-to-radial-ray trick gives analytic nearest orbit:
|
|
34
|
+
// phi = atan((sx+sy)/B, sx-sy) [orbital angle, r-invariant]
|
|
35
|
+
// K = 0.7071*(cos φ+B·sin φ, -cos φ+B·sin φ) [radial direction on ellipse]
|
|
36
|
+
// r0 = dot(uv, K) / dot(K,K) [nearest orbital radius]
|
|
37
|
+
// dr = length(uv - r0·K) [perpendicular disk offset]
|
|
38
|
+
vec3 diskSample(vec2 uv) {
|
|
39
|
+
float B = 0.15; // near edge-on (~9° from edge) → Saturn-like flat stripe
|
|
40
|
+
float phi = atan((uv.x + uv.y) / B, uv.x - uv.y);
|
|
41
|
+
vec2 K = vec2(0.7071 * (cos(phi) + B * sin(phi)),
|
|
42
|
+
0.7071 * (-cos(phi) + B * sin(phi)));
|
|
43
|
+
float r0 = dot(uv, K) / dot(K, K);
|
|
44
|
+
float dr = length(uv - r0 * K);
|
|
45
|
+
|
|
46
|
+
// Radial extent: inner edge near photon sphere, outer at 0.65
|
|
47
|
+
float radial = smoothstep(0.185, 0.235, r0) * smoothstep(0.65, 0.50, r0);
|
|
48
|
+
|
|
49
|
+
// Disk height (thin, crisp; spaghettifies near horizon)
|
|
50
|
+
float fall = smoothstep(0.30, 0.19, length(uv));
|
|
51
|
+
float sigma = (0.012 + r0 * 0.022) * max(0.07, 1.0 - fall * 0.93);
|
|
52
|
+
float height = exp(-pow(dr / sigma, 2.0));
|
|
53
|
+
|
|
54
|
+
// Gas-flow striations along orbital direction
|
|
55
|
+
float fiber = 0.62 + 0.38 * abs(sin(phi * 18.0 + r0 * 22.0 - uTime * 0.25));
|
|
56
|
+
|
|
57
|
+
// Doppler: approaching half (sin φ > 0) → brighter
|
|
58
|
+
float dop = 0.30 + 1.40 * max(0.0, sin(phi));
|
|
59
|
+
|
|
60
|
+
// Color: inner cream-white → gold → dark orange outer
|
|
61
|
+
float t = clamp((r0 - 0.18) / (0.62 - 0.18), 0.0, 1.0);
|
|
62
|
+
vec3 col;
|
|
63
|
+
if (t < 0.25) {
|
|
64
|
+
col = mix(vec3(1.00, 0.97, 0.85), vec3(1.00, 0.72, 0.18), t / 0.25);
|
|
65
|
+
} else {
|
|
66
|
+
col = mix(vec3(1.00, 0.72, 0.18), vec3(0.38, 0.12, 0.01), (t - 0.25) / 0.75);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Tidal brightening near inner edge (kept subtle to avoid drowning the corona)
|
|
70
|
+
float innerBoost = 1.0 + 0.5 * exp(-pow((r0 - 0.21) / 0.10, 2.0));
|
|
71
|
+
|
|
72
|
+
return col * (height * radial * fiber * dop * innerBoost);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
void main() {
|
|
76
|
+
vec4 o = vec4(0.0);
|
|
77
|
+
float e = 0.0, R = 0.0;
|
|
78
|
+
vec3 q = vec3(0.0), p = vec3(0.0);
|
|
79
|
+
vec3 d = vec3((gl_FragCoord.xy - 0.5 * uResolution) / uResolution.y, 0.7);
|
|
80
|
+
q.z -= 1.0;
|
|
81
|
+
|
|
82
|
+
for (float i = 0.0; i < 33.0; i += 1.0) {
|
|
83
|
+
// Corrupted-theme quasar palette:
|
|
84
|
+
// 0–25% early iters (dim) → magenta outer glow [0.83–0.88]
|
|
85
|
+
// 25–70% mid iters → purple body [0.65–0.74]
|
|
86
|
+
// 70–85% bright inner iters → magenta burst [0.82–0.88]
|
|
87
|
+
// 85–100% late iters (high e)→ gold/yellow sparks [0.14–0.19]
|
|
88
|
+
float t = fract(i / 33.0);
|
|
89
|
+
float base;
|
|
90
|
+
if (t < 0.25) {
|
|
91
|
+
base = mix(0.83, 0.88, t / 0.25);
|
|
92
|
+
} else if (t < 0.70) {
|
|
93
|
+
base = mix(0.65, 0.74, (t - 0.25) / 0.45);
|
|
94
|
+
} else if (t < 0.85) {
|
|
95
|
+
base = mix(0.82, 0.88, (t - 0.70) / 0.15);
|
|
96
|
+
} else {
|
|
97
|
+
base = mix(0.14, 0.19, (t - 0.85) / 0.15);
|
|
98
|
+
}
|
|
99
|
+
float h = (uHue >= 0.0) ? uHue : base + p.y * 0.04;
|
|
100
|
+
o.rgb += hsv(h, clamp(e * 0.4, 0.0, 1.0), e / 30.0 * uIntensity);
|
|
101
|
+
|
|
102
|
+
p = q += d * max(e, 0.01) * R * 0.14;
|
|
103
|
+
p.xy *= rotate2D(0.8 * uRotationRate);
|
|
104
|
+
|
|
105
|
+
R = length(p);
|
|
106
|
+
float newPy = -p.z / R - 0.8;
|
|
107
|
+
e = newPy;
|
|
108
|
+
p = vec3(log2(R) + uTime, newPy, atan(p.x * 0.08, p.y) - uTime * 0.2);
|
|
109
|
+
|
|
110
|
+
float s = 1.0;
|
|
111
|
+
for (int si = 0; si < 10; si++) {
|
|
112
|
+
e += abs(dot(sin(p.yzx * s), cos(p.yyz * s))) / s;
|
|
113
|
+
s += s;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Reinhard + contrast curve: compress sum then push dim areas toward black.
|
|
118
|
+
// pow(1.8) is softer than 2.2 — keeps the magenta/purple corona visible
|
|
119
|
+
// while still collapsing near-zero cloud artifacts to black.
|
|
120
|
+
o.rgb = o.rgb / (1.0 + o.rgb);
|
|
121
|
+
o.rgb = pow(o.rgb, vec3(1.8));
|
|
122
|
+
|
|
123
|
+
// Near/far depth split: project onto the 45° CW major axis direction (1,-1)/√2.
|
|
124
|
+
// majorProj > 0 → lower-right arm → near (passes in front of BH)
|
|
125
|
+
// majorProj < 0 → upper-left arm → far (passes behind BH)
|
|
126
|
+
float dist = length(d.xy);
|
|
127
|
+
float majorProj = dot(d.xy, vec2(0.7071, -0.7071));
|
|
128
|
+
float nearMask = smoothstep(-0.04, 0.04, majorProj);
|
|
129
|
+
vec3 diskVal = diskSample(d.xy) * 2.0 * uIntensity;
|
|
130
|
+
|
|
131
|
+
// Far arm: add before shadow; extra depth-fade darkens it near the BH centre
|
|
132
|
+
float farDepth = smoothstep(0.12, 0.26, dist);
|
|
133
|
+
o.rgb += diskVal * (1.0 - nearMask) * farDepth;
|
|
134
|
+
|
|
135
|
+
// Black-hole event horizon: shadows the vortex cloud (and the far arm)
|
|
136
|
+
float shadow = smoothstep(0.12, 0.18, dist);
|
|
137
|
+
float ring = exp(-pow((dist - 0.18) * 30.0, 2.0)) * 0.9;
|
|
138
|
+
o.rgb = o.rgb * shadow + vec3(ring, ring * 0.80, ring * 0.45);
|
|
139
|
+
|
|
140
|
+
// Near arm: add after shadow → visibly crosses in front of the event horizon
|
|
141
|
+
o.rgb += diskVal * nearMask;
|
|
142
|
+
|
|
143
|
+
// Lensed arc: thin bright stripe above the shadow boundary
|
|
144
|
+
float lensThe = atan(d.y, d.x);
|
|
145
|
+
float bend = exp(-(dist - 0.19) * 9.0);
|
|
146
|
+
float thetaS = lensThe + 3.14159 * bend;
|
|
147
|
+
vec2 uvLens = vec2(cos(thetaS), sin(thetaS)) * dist;
|
|
148
|
+
float lensFade = exp(-pow((dist - 0.22) * 16.0, 2.0))
|
|
149
|
+
* smoothstep(0.03, 0.08, d.y);
|
|
150
|
+
o.rgb += diskSample(uvLens) * lensFade * 0.5 * uIntensity;
|
|
151
|
+
|
|
152
|
+
// Magenta corona: tight photon-ring — width 50 gives ~0.033 FWHM, <4% bleed into shadow
|
|
153
|
+
float coronaAmt = exp(-pow((dist - 0.21) * 50.0, 2.0)) * 0.7;
|
|
154
|
+
o.rgb += vec3(coronaAmt, 0.0, coronaAmt) * uIntensity;
|
|
155
|
+
|
|
156
|
+
o.a = 1.0;
|
|
157
|
+
gl_FragColor = clamp(o, 0.0, 1.0);
|
|
158
|
+
}
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
class CorruptedVortex {
|
|
162
|
+
constructor(canvas, options = {}) {
|
|
163
|
+
this.canvas = canvas;
|
|
164
|
+
this.options = {
|
|
165
|
+
speed: options.speed ?? 1.0,
|
|
166
|
+
intensity: options.intensity ?? 1.0,
|
|
167
|
+
rotationRate: options.rotationRate ?? 1.0,
|
|
168
|
+
hue: options.hue ?? null,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
this.gl = null;
|
|
172
|
+
this.program = null;
|
|
173
|
+
this.uniforms = {};
|
|
174
|
+
this.buffer = null;
|
|
175
|
+
this._rafId = null;
|
|
176
|
+
this._isRunning = false;
|
|
177
|
+
this._elapsed = 0;
|
|
178
|
+
this._lastTs = null;
|
|
179
|
+
this._resizeObserver = null;
|
|
180
|
+
this._intersectionObserver = null;
|
|
181
|
+
|
|
182
|
+
this.init();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
_compileShader(type, src) {
|
|
186
|
+
const gl = this.gl;
|
|
187
|
+
const shader = gl.createShader(type);
|
|
188
|
+
gl.shaderSource(shader, src);
|
|
189
|
+
gl.compileShader(shader);
|
|
190
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
191
|
+
console.warn('CorruptedVortex: shader compile failed\n', gl.getShaderInfoLog(shader));
|
|
192
|
+
gl.deleteShader(shader);
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
return shader;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
init() {
|
|
199
|
+
const gl = this.canvas.getContext('webgl', { alpha: false });
|
|
200
|
+
if (!gl) {
|
|
201
|
+
console.warn('CorruptedVortex: WebGL not supported in this browser.');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
this.gl = gl;
|
|
205
|
+
|
|
206
|
+
const vs = this._compileShader(gl.VERTEX_SHADER, VERT_SRC);
|
|
207
|
+
const fs = this._compileShader(gl.FRAGMENT_SHADER, FRAG_SRC);
|
|
208
|
+
if (!vs || !fs) { this.destroy(); return; }
|
|
209
|
+
|
|
210
|
+
this.program = gl.createProgram();
|
|
211
|
+
gl.attachShader(this.program, vs);
|
|
212
|
+
gl.attachShader(this.program, fs);
|
|
213
|
+
gl.linkProgram(this.program);
|
|
214
|
+
gl.deleteShader(vs);
|
|
215
|
+
gl.deleteShader(fs);
|
|
216
|
+
|
|
217
|
+
if (!gl.getProgramParameter(this.program, gl.LINK_STATUS)) {
|
|
218
|
+
console.warn('CorruptedVortex: program link failed\n', gl.getProgramInfoLog(this.program));
|
|
219
|
+
this.destroy();
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
gl.useProgram(this.program);
|
|
224
|
+
|
|
225
|
+
this.uniforms = {
|
|
226
|
+
resolution: gl.getUniformLocation(this.program, 'uResolution'),
|
|
227
|
+
time: gl.getUniformLocation(this.program, 'uTime'),
|
|
228
|
+
intensity: gl.getUniformLocation(this.program, 'uIntensity'),
|
|
229
|
+
rotationRate: gl.getUniformLocation(this.program, 'uRotationRate'),
|
|
230
|
+
hue: gl.getUniformLocation(this.program, 'uHue'),
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Fullscreen triangle — one triangle covers the full NDC square
|
|
234
|
+
this.buffer = gl.createBuffer();
|
|
235
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer);
|
|
236
|
+
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 3, -1, -1, 3]), gl.STATIC_DRAW);
|
|
237
|
+
|
|
238
|
+
const posLoc = gl.getAttribLocation(this.program, 'aPosition');
|
|
239
|
+
gl.enableVertexAttribArray(posLoc);
|
|
240
|
+
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
|
|
241
|
+
|
|
242
|
+
this._resizeObserver = new ResizeObserver(() => this._resize());
|
|
243
|
+
this._resizeObserver.observe(this.canvas);
|
|
244
|
+
|
|
245
|
+
this._intersectionObserver = new IntersectionObserver(entries => {
|
|
246
|
+
if (entries[0].isIntersecting) {
|
|
247
|
+
this.start();
|
|
248
|
+
} else {
|
|
249
|
+
this.stop();
|
|
250
|
+
}
|
|
251
|
+
}, { threshold: 0.1 });
|
|
252
|
+
this._intersectionObserver.observe(this.canvas);
|
|
253
|
+
|
|
254
|
+
this._resize();
|
|
255
|
+
this.start();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
_resize() {
|
|
259
|
+
const gl = this.gl;
|
|
260
|
+
if (!gl || !this.canvas) return;
|
|
261
|
+
const dpr = 0.5; // half-res: GPU renders fewer pixels, CSS scales up
|
|
262
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
263
|
+
if (rect.width === 0 || rect.height === 0) return;
|
|
264
|
+
this.canvas.width = Math.round(rect.width * dpr);
|
|
265
|
+
this.canvas.height = Math.round(rect.height * dpr);
|
|
266
|
+
gl.viewport(0, 0, this.canvas.width, this.canvas.height);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
_render(ts) {
|
|
270
|
+
if (!this._isRunning) return;
|
|
271
|
+
|
|
272
|
+
// Throttle to ~30fps to keep GPU load manageable
|
|
273
|
+
if (this._lastTs !== null && ts - this._lastTs < 33) {
|
|
274
|
+
this._rafId = requestAnimationFrame(ts => this._render(ts));
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (this._lastTs !== null) {
|
|
279
|
+
this._elapsed += (ts - this._lastTs) / 1000.0;
|
|
280
|
+
}
|
|
281
|
+
this._lastTs = ts;
|
|
282
|
+
|
|
283
|
+
const gl = this.gl;
|
|
284
|
+
gl.useProgram(this.program);
|
|
285
|
+
gl.uniform2f(this.uniforms.resolution, this.canvas.width, this.canvas.height);
|
|
286
|
+
gl.uniform1f(this.uniforms.time, this._elapsed * this.options.speed);
|
|
287
|
+
gl.uniform1f(this.uniforms.intensity, this.options.intensity);
|
|
288
|
+
gl.uniform1f(this.uniforms.rotationRate, this.options.rotationRate);
|
|
289
|
+
gl.uniform1f(this.uniforms.hue, this.options.hue !== null ? this.options.hue : -1.0);
|
|
290
|
+
|
|
291
|
+
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
|
292
|
+
|
|
293
|
+
this._rafId = requestAnimationFrame(ts => this._render(ts));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
start() {
|
|
297
|
+
if (this._isRunning || !this.gl) return;
|
|
298
|
+
this._isRunning = true;
|
|
299
|
+
this._lastTs = null;
|
|
300
|
+
this._rafId = requestAnimationFrame(ts => this._render(ts));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
stop() {
|
|
304
|
+
this._isRunning = false;
|
|
305
|
+
this._lastTs = null;
|
|
306
|
+
if (this._rafId) {
|
|
307
|
+
cancelAnimationFrame(this._rafId);
|
|
308
|
+
this._rafId = null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
destroy() {
|
|
313
|
+
this.stop();
|
|
314
|
+
const gl = this.gl;
|
|
315
|
+
if (gl) {
|
|
316
|
+
if (this.program) gl.deleteProgram(this.program);
|
|
317
|
+
if (this.buffer) gl.deleteBuffer(this.buffer);
|
|
318
|
+
}
|
|
319
|
+
if (this._resizeObserver) { this._resizeObserver.disconnect(); this._resizeObserver = null; }
|
|
320
|
+
if (this._intersectionObserver) { this._intersectionObserver.disconnect(); this._intersectionObserver = null; }
|
|
321
|
+
this.gl = null;
|
|
322
|
+
this.canvas = null;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Export for manual use / build pipelines
|
|
327
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
328
|
+
module.exports = { CorruptedVortex };
|
|
329
|
+
}
|
package/docs/ROADMAP.md
DELETED
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
# CelesteCLI Roadmap
|
|
2
|
-
|
|
3
|
-
This document tracks planned features, enhancements, and potential migrations for CelesteCLI.
|
|
4
|
-
|
|
5
|
-
## Current Status (December 2025)
|
|
6
|
-
|
|
7
|
-
### ✅ Completed
|
|
8
|
-
- Enterprise-grade RPG menu system with contextual help
|
|
9
|
-
- 18 AI-powered skills via function calling
|
|
10
|
-
- Multi-provider support (OpenAI, Grok, Venice, Anthropic, OpenRouter, DigitalOcean)
|
|
11
|
-
- Vertex AI support via OpenAI-compatible endpoint
|
|
12
|
-
- Session persistence and auto-save
|
|
13
|
-
- NSFW mode with Venice.ai image generation
|
|
14
|
-
- Context-aware /safe command
|
|
15
|
-
- Tool result filtering (hide raw JSON from UI)
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Short-Term (Q1 2025)
|
|
20
|
-
|
|
21
|
-
### High Priority
|
|
22
|
-
|
|
23
|
-
#### 1. Vertex AI (Google Cloud) Setup Guide
|
|
24
|
-
- **Status**: Requires complex Google Cloud configuration
|
|
25
|
-
- **Goal**: Document complete Vertex AI setup for enterprise users
|
|
26
|
-
- **Provider Clarification**:
|
|
27
|
-
- **Gemini AI (AI Studio)**: Simple API keys ✅ READY
|
|
28
|
-
- **Vertex AI (Google Cloud)**: OAuth2 tokens, requires setup ⚠️ ENTERPRISE ONLY
|
|
29
|
-
|
|
30
|
-
**Vertex AI Requirements:**
|
|
31
|
-
1. **Google Cloud Project**: Active GCP project with billing enabled
|
|
32
|
-
2. **Vertex AI API**: Enable Vertex AI API in GCP console
|
|
33
|
-
3. **Service Account**: Create service account with `Vertex AI User` role
|
|
34
|
-
4. **Authentication**: Use OAuth2 tokens (NOT simple API keys)
|
|
35
|
-
5. **Token Management**: Tokens expire after 1 hour, need refresh mechanism
|
|
36
|
-
|
|
37
|
-
**Endpoint URL Format:**
|
|
38
|
-
```
|
|
39
|
-
https://aiplatform.googleapis.com/v1/projects/{PROJECT_ID}/locations/{LOCATION}/endpoints/openapi
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
**Configuration Example:**
|
|
43
|
-
```json
|
|
44
|
-
{
|
|
45
|
-
"api_key": "ya29.c.c0ASRK0Ga...", // OAuth2 access token
|
|
46
|
-
"base_url": "https://aiplatform.googleapis.com/v1/projects/my-project/locations/us-central1/endpoints/openapi",
|
|
47
|
-
"model": "gemini-2.0-flash",
|
|
48
|
-
"timeout": 60
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
**Token Generation (Go example):**
|
|
53
|
-
```go
|
|
54
|
-
import "golang.org/x/oauth2/google"
|
|
55
|
-
|
|
56
|
-
creds, _ := google.FindDefaultCredentials(ctx, "https://www.googleapis.com/auth/cloud-platform")
|
|
57
|
-
token, _ := creds.TokenSource.Token()
|
|
58
|
-
config.APIKey = token.AccessToken
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
**Why Most Users Should Use Gemini Instead:**
|
|
62
|
-
- Gemini AI: Simple API key from https://aistudio.google.com/apikey
|
|
63
|
-
- Vertex AI: Requires GCP project, billing, OAuth2 setup
|
|
64
|
-
- Both use same Gemini models, same function calling capabilities
|
|
65
|
-
- Vertex AI is for enterprise users with existing GCP infrastructure
|
|
66
|
-
|
|
67
|
-
- **Decision**: Gemini AI is the recommended provider for most users
|
|
68
|
-
|
|
69
|
-
#### 2. Improve Error Messaging
|
|
70
|
-
- **Goal**: Better error messages for common issues
|
|
71
|
-
- **Tasks**:
|
|
72
|
-
- [ ] API key validation on startup
|
|
73
|
-
- [ ] Clear messages for unsupported model features
|
|
74
|
-
- [ ] Network timeout handling with retry suggestions
|
|
75
|
-
- [ ] Configuration validation with fix suggestions
|
|
76
|
-
|
|
77
|
-
#### 3. Performance Optimization
|
|
78
|
-
- **Goal**: Reduce latency and improve responsiveness
|
|
79
|
-
- **Tasks**:
|
|
80
|
-
- [ ] Stream parsing optimization
|
|
81
|
-
- [ ] Reduce re-renders in TUI
|
|
82
|
-
- [ ] Cache provider model lists
|
|
83
|
-
- [ ] Lazy-load skill definitions
|
|
84
|
-
|
|
85
|
-
### Medium Priority
|
|
86
|
-
|
|
87
|
-
#### 4. Enhanced Skill Management
|
|
88
|
-
- **Goal**: Better skill discovery and usage
|
|
89
|
-
- **Tasks**:
|
|
90
|
-
- [ ] /skills menu shows skill descriptions on hover/selection
|
|
91
|
-
- [ ] Skill usage examples in help text
|
|
92
|
-
- [ ] Skill execution history (recent skills used)
|
|
93
|
-
- [ ] Skill favorites/pinning
|
|
94
|
-
|
|
95
|
-
#### 5. Configuration Management
|
|
96
|
-
- **Goal**: Easier config switching and management
|
|
97
|
-
- **Tasks**:
|
|
98
|
-
- [ ] /config list shows model details and capabilities
|
|
99
|
-
- [ ] /config test <profile> - test API connectivity
|
|
100
|
-
- [ ] Config templates for common providers
|
|
101
|
-
- [ ] Import/export config profiles
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## Medium-Term (Q2-Q3 2025)
|
|
106
|
-
|
|
107
|
-
### Research & Evaluation
|
|
108
|
-
|
|
109
|
-
#### 6. Native Vertex AI SDK Migration (Option B)
|
|
110
|
-
- **Status**: Documented as potential enhancement
|
|
111
|
-
- **Trigger**: If Google deprecates OpenAI-compatible endpoint OR performance benefits are significant
|
|
112
|
-
- **Complexity**: MEDIUM (8-12 hours estimated)
|
|
113
|
-
- **Impact**: Better long-term stability, native Gemini features
|
|
114
|
-
|
|
115
|
-
**Migration Plan**:
|
|
116
|
-
|
|
117
|
-
##### Phase 1: Research & Prototyping (2-3 hours)
|
|
118
|
-
- [ ] Set up native genai SDK in test branch
|
|
119
|
-
- [ ] Prototype skill definition conversion (JSON → genai.FunctionDeclaration)
|
|
120
|
-
- [ ] Test response parsing (functionCall vs tool_calls)
|
|
121
|
-
- [ ] Benchmark performance vs OpenAI-compatible endpoint
|
|
122
|
-
|
|
123
|
-
##### Phase 2: Abstraction Layer (3-4 hours)
|
|
124
|
-
- [ ] Create provider abstraction interface
|
|
125
|
-
- [ ] Implement OpenAI provider adapter
|
|
126
|
-
- [ ] Implement native Gemini provider adapter
|
|
127
|
-
- [ ] Unified response format converter
|
|
128
|
-
|
|
129
|
-
##### Phase 3: Testing & Validation (2-3 hours)
|
|
130
|
-
- [ ] Test all 18 skills with native SDK
|
|
131
|
-
- [ ] Validate function calling accuracy
|
|
132
|
-
- [ ] Test multi-turn conversations
|
|
133
|
-
- [ ] Compare performance metrics
|
|
134
|
-
|
|
135
|
-
##### Phase 4: Migration (1-2 hours)
|
|
136
|
-
- [ ] Switch default Vertex AI provider to native SDK
|
|
137
|
-
- [ ] Update documentation
|
|
138
|
-
- [ ] Deprecation notice for OpenAI-compatible endpoint
|
|
139
|
-
|
|
140
|
-
**Code Structure Example**:
|
|
141
|
-
```go
|
|
142
|
-
// Future architecture with provider abstraction
|
|
143
|
-
|
|
144
|
-
type Provider interface {
|
|
145
|
-
SendMessage(ctx context.Context, messages []Message, tools []Tool) (*Response, error)
|
|
146
|
-
SupportsFunctionCalling() bool
|
|
147
|
-
ConvertSkillDefinition(skill SkillDefinition) interface{}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
type OpenAIProvider struct {
|
|
151
|
-
client *openai.Client
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
type GeminiProvider struct {
|
|
155
|
-
client *genai.Client
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Skills remain unchanged - adapters handle conversion
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
**Decision Criteria**:
|
|
162
|
-
- Google announces deprecation of OpenAI endpoint → Migrate immediately
|
|
163
|
-
- Native SDK shows >20% performance improvement → Evaluate migration
|
|
164
|
-
- Native SDK enables exclusive features → Evaluate value vs effort
|
|
165
|
-
- Community reports OpenAI endpoint issues → Migrate proactively
|
|
166
|
-
|
|
167
|
-
**Resources**:
|
|
168
|
-
- [Google GenAI SDK Docs](https://pkg.go.dev/google.golang.org/genai)
|
|
169
|
-
- [Function Calling Guide](https://ai.google.dev/gemini-api/docs/function-calling)
|
|
170
|
-
- [Migration Guide](https://ai.google.dev/gemini-api/docs/migrate)
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
### Feature Enhancements
|
|
175
|
-
|
|
176
|
-
#### 7. Advanced Skill Features
|
|
177
|
-
- **Goal**: More powerful AI capabilities
|
|
178
|
-
- **Tasks**:
|
|
179
|
-
- [ ] Parallel skill execution (when LLM calls multiple)
|
|
180
|
-
- [ ] Skill chaining (output of one feeds another)
|
|
181
|
-
- [ ] Custom user-defined skills (plugin system)
|
|
182
|
-
- [ ] Skill templates and generators
|
|
183
|
-
|
|
184
|
-
#### 8. Conversation Management
|
|
185
|
-
- **Goal**: Better session handling
|
|
186
|
-
- **Tasks**:
|
|
187
|
-
- [ ] Named sessions (save/load by name)
|
|
188
|
-
- [ ] Session search (find past conversations)
|
|
189
|
-
- [ ] Session export (markdown, JSON)
|
|
190
|
-
- [ ] Session branching (fork conversation)
|
|
191
|
-
|
|
192
|
-
#### 9. Multi-Modal Support
|
|
193
|
-
- **Goal**: Beyond text interactions
|
|
194
|
-
- **Tasks**:
|
|
195
|
-
- [ ] Image input support (upload images for analysis)
|
|
196
|
-
- [ ] File attachment support
|
|
197
|
-
- [ ] Voice input/output (ElevenLabs integration)
|
|
198
|
-
- [ ] PDF parsing and analysis
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
## Long-Term (Q4 2025+)
|
|
203
|
-
|
|
204
|
-
### Major Features
|
|
205
|
-
|
|
206
|
-
#### 10. Team/Collaboration Features
|
|
207
|
-
- **Goal**: Multi-user support
|
|
208
|
-
- **Tasks**:
|
|
209
|
-
- [ ] Shared sessions
|
|
210
|
-
- [ ] Team workspaces
|
|
211
|
-
- [ ] Permission management
|
|
212
|
-
- [ ] Audit logs
|
|
213
|
-
|
|
214
|
-
#### 11. Cloud Sync
|
|
215
|
-
- **Goal**: Cross-device session sync
|
|
216
|
-
- **Tasks**:
|
|
217
|
-
- [ ] Optional cloud backup
|
|
218
|
-
- [ ] Encrypted session storage
|
|
219
|
-
- [ ] Device sync
|
|
220
|
-
- [ ] Web UI companion
|
|
221
|
-
|
|
222
|
-
#### 12. Advanced Analytics
|
|
223
|
-
- **Goal**: Usage insights
|
|
224
|
-
- **Tasks**:
|
|
225
|
-
- [ ] Token usage tracking
|
|
226
|
-
- [ ] Cost estimation per provider
|
|
227
|
-
- [ ] Skill usage analytics
|
|
228
|
-
- [ ] Performance metrics dashboard
|
|
229
|
-
|
|
230
|
-
---
|
|
231
|
-
|
|
232
|
-
## Deprecated/Archived
|
|
233
|
-
|
|
234
|
-
### Considered but Deferred
|
|
235
|
-
- **Local LLM support**: Complexity vs benefit ratio too high for v1
|
|
236
|
-
- **Browser extension**: Scope too large, focus on CLI first
|
|
237
|
-
- **Mobile app**: Desktop/terminal focus more valuable
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
## Contributing to Roadmap
|
|
242
|
-
|
|
243
|
-
This roadmap is living document. To propose additions:
|
|
244
|
-
|
|
245
|
-
1. Check existing items to avoid duplicates
|
|
246
|
-
2. Open GitHub issue with `[Roadmap]` prefix
|
|
247
|
-
3. Describe:
|
|
248
|
-
- Use case and problem being solved
|
|
249
|
-
- Estimated complexity (LOW/MEDIUM/HIGH)
|
|
250
|
-
- Impact on existing features
|
|
251
|
-
- User benefit
|
|
252
|
-
|
|
253
|
-
Priority is determined by:
|
|
254
|
-
- User requests and feedback
|
|
255
|
-
- Technical dependencies
|
|
256
|
-
- Maintenance burden
|
|
257
|
-
- Strategic value
|
|
258
|
-
|
|
259
|
-
---
|
|
260
|
-
|
|
261
|
-
## Version History
|
|
262
|
-
|
|
263
|
-
- **v0.1** (Dec 2025): Initial roadmap creation
|
|
264
|
-
- Enterprise menu system complete
|
|
265
|
-
- Vertex AI OpenAI-compatible endpoint implemented
|
|
266
|
-
- Native SDK migration documented as Option B
|