@toddzheng024/dscode-bundle 0.7.12 → 0.7.14
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/THIRD_PARTY_NOTICES.md +3 -3
- package/cordis.patch.yml +6 -2
- package/package.json +7 -5
- package/plugins/code-review/index.mjs +5 -2
- package/plugins/compaction/threshold.mjs +12 -0
- package/plugins/credentials/index.mjs +13 -0
- package/plugins/grok/adapter.mjs +114 -0
- package/plugins/grok/auth.mjs +76 -0
- package/plugins/grok/billing.mjs +146 -0
- package/plugins/grok/index.mjs +118 -0
- package/plugins/grok/models.mjs +129 -0
- package/plugins/grok/status.mjs +28 -0
- package/plugins/grok/wire.mjs +268 -0
- package/plugins/i18n/messages.mjs +6 -6
- package/plugins/providers/catalog.mjs +2 -0
- package/plugins/session-metrics/view.mjs +34 -3
- package/vendor/compaction-basic/index.js +91 -1
- package/vendor/persistent/index.js +10 -9
- package/vendor/terminal/index.js +5 -0
- package/vendor/tui/lib/app.mjs +6711 -0
- package/vendor/tui/lib/approval.mjs +122 -0
- package/vendor/tui/lib/attachments.mjs +218 -0
- package/vendor/tui/lib/authorization-panel.mjs +242 -0
- package/vendor/tui/lib/authorization.mjs +111 -0
- package/vendor/tui/lib/commands.mjs +123 -0
- package/vendor/tui/lib/dscode/chat.mjs +84 -0
- package/vendor/tui/lib/dscode/flags.mjs +32 -0
- package/vendor/tui/lib/dscode/model-search.mjs +68 -0
- package/vendor/tui/lib/dscode/paste.mjs +87 -0
- package/vendor/tui/lib/dscode/telemetry.mjs +82 -0
- package/vendor/tui/lib/dscode/welcome.mjs +100 -0
- package/vendor/tui/lib/editor-keys.mjs +346 -0
- package/vendor/tui/lib/editor.mjs +50 -0
- package/vendor/tui/lib/fork.mjs +24 -0
- package/vendor/tui/lib/git-workflow.mjs +278 -0
- package/vendor/tui/{types/history.d.ts → lib/history.mjs} +69 -26
- package/vendor/tui/{types/i18n.d.ts → lib/i18n.mjs} +23 -15
- package/vendor/tui/lib/index.mjs +2200 -0
- package/vendor/tui/lib/input-split.mjs +170 -0
- package/vendor/tui/lib/internals.mjs +90 -0
- package/vendor/tui/lib/invariant.mjs +22 -0
- package/vendor/tui/lib/kernel-panels.mjs +1265 -0
- package/vendor/tui/lib/keyboard.mjs +283 -0
- package/vendor/tui/lib/language-panel.mjs +41 -0
- package/vendor/tui/lib/locales/en.mjs +495 -0
- package/vendor/tui/lib/locales/zh.mjs +495 -0
- package/vendor/tui/lib/mentions.mjs +150 -0
- package/vendor/tui/lib/model-capabilities.mjs +245 -0
- package/vendor/tui/lib/models.mjs +215 -0
- package/vendor/tui/{types/panel-accent.d.ts → lib/panel-accent.mjs} +11 -7
- package/vendor/tui/lib/permissions.mjs +45 -0
- package/vendor/tui/lib/plugin-inventory.mjs +27 -0
- package/vendor/tui/lib/presets.mjs +46 -0
- package/vendor/tui/lib/provider-settings.mjs +600 -0
- package/vendor/tui/lib/questions.mjs +116 -0
- package/vendor/tui/lib/rainbow.mjs +181 -0
- package/vendor/tui/lib/render/animations.mjs +665 -0
- package/vendor/tui/lib/render/editor.mjs +437 -0
- package/vendor/tui/lib/render/export.mjs +119 -0
- package/vendor/tui/lib/render/fuzzy.mjs +79 -0
- package/vendor/tui/lib/render/ime-cursor.mjs +135 -0
- package/vendor/tui/lib/render/inspector.mjs +93 -0
- package/vendor/tui/lib/render/lines.mjs +538 -0
- package/vendor/tui/lib/render/markdown.mjs +592 -0
- package/vendor/tui/lib/render/projection.mjs +1864 -0
- package/vendor/tui/lib/render/status.mjs +591 -0
- package/vendor/tui/lib/render/text.mjs +157 -0
- package/vendor/tui/lib/render/tool-detail.mjs +187 -0
- package/vendor/tui/lib/render/tool-preview.mjs +79 -0
- package/vendor/tui/lib/render/usage.mjs +336 -0
- package/vendor/tui/lib/render/width.mjs +191 -0
- package/vendor/tui/lib/session-directory.mjs +329 -0
- package/vendor/tui/lib/session-query.mjs +184 -0
- package/vendor/tui/lib/session-switch.mjs +51 -0
- package/vendor/tui/lib/settings-file.mjs +75 -0
- package/vendor/tui/lib/skills.mjs +119 -0
- package/vendor/tui/lib/startup.mjs +111 -0
- package/vendor/tui/lib/store.mjs +129 -0
- package/vendor/tui/lib/subagents.mjs +200 -0
- package/vendor/tui/lib/terminal-title.mjs +186 -0
- package/vendor/tui/lib/theme-panel.mjs +60 -0
- package/vendor/tui/lib/theme.mjs +379 -0
- package/vendor/tui/lib/update-panel.mjs +239 -0
- package/vendor/tui/lib/update.mjs +101 -0
- package/vendor/tui/lib/version.mjs +105 -0
- package/vendor/tui/lib/whale-glyph.mjs +20 -0
- package/vendor/tui/LICENSE +0 -21
- package/vendor/tui/devtools-CdTl3MNy.mjs +0 -3643
- package/vendor/tui/dscode-email/cli.mjs +0 -32
- package/vendor/tui/dscode-email/contacts.mjs +0 -36
- package/vendor/tui/dscode-email/gmail-oauth.mjs +0 -69
- package/vendor/tui/dscode-email/gmail-store.mjs +0 -2
- package/vendor/tui/dscode-email/gmail.mjs +0 -194
- package/vendor/tui/dscode-email/imap.mjs +0 -136
- package/vendor/tui/dscode-email/inbox.d.mts +0 -25
- package/vendor/tui/dscode-email/inbox.mjs +0 -76
- package/vendor/tui/dscode-email/smtp.mjs +0 -69
- package/vendor/tui/dscode-email/store.mjs +0 -32
- package/vendor/tui/dscode-providers/catalog.mjs +0 -136
- package/vendor/tui/dscode-providers/effort.mjs +0 -35
- package/vendor/tui/dscode-providers/openrouter-account.mjs +0 -171
- package/vendor/tui/index.mjs +0 -44441
- package/vendor/tui/invariant.mjs +0 -21
- package/vendor/tui/rolldown-runtime-CMFfr-1z.mjs +0 -26
- package/vendor/tui/session-query.mjs +0 -150
- package/vendor/tui/startup.mjs +0 -109
- package/vendor/tui/theme-7u5Qo3dF.mjs +0 -1265
- package/vendor/tui/types/app.d.ts +0 -348
- package/vendor/tui/types/approval.d.ts +0 -59
- package/vendor/tui/types/attachments.d.ts +0 -52
- package/vendor/tui/types/authorization-panel.d.ts +0 -22
- package/vendor/tui/types/authorization.d.ts +0 -36
- package/vendor/tui/types/commands.d.ts +0 -52
- package/vendor/tui/types/editor-keys.d.ts +0 -105
- package/vendor/tui/types/editor.d.ts +0 -6
- package/vendor/tui/types/fork.d.ts +0 -8
- package/vendor/tui/types/git-workflow.d.ts +0 -121
- package/vendor/tui/types/index.d.ts +0 -223
- package/vendor/tui/types/input-split.d.ts +0 -54
- package/vendor/tui/types/internals.d.ts +0 -26
- package/vendor/tui/types/invariant.d.ts +0 -15
- package/vendor/tui/types/kernel-panels.d.ts +0 -245
- package/vendor/tui/types/keyboard.d.ts +0 -80
- package/vendor/tui/types/language-panel.d.ts +0 -12
- package/vendor/tui/types/locales/en.d.ts +0 -450
- package/vendor/tui/types/locales/zh.d.ts +0 -9
- package/vendor/tui/types/mentions.d.ts +0 -85
- package/vendor/tui/types/model-capabilities.d.ts +0 -82
- package/vendor/tui/types/models.d.ts +0 -133
- package/vendor/tui/types/permissions.d.ts +0 -27
- package/vendor/tui/types/plugin-inventory.d.ts +0 -11
- package/vendor/tui/types/presets.d.ts +0 -22
- package/vendor/tui/types/provider-settings.d.ts +0 -239
- package/vendor/tui/types/questions.d.ts +0 -54
- package/vendor/tui/types/rainbow.d.ts +0 -69
- package/vendor/tui/types/render/animations.d.ts +0 -307
- package/vendor/tui/types/render/editor.d.ts +0 -163
- package/vendor/tui/types/render/export.d.ts +0 -9
- package/vendor/tui/types/render/fuzzy.d.ts +0 -21
- package/vendor/tui/types/render/ime-cursor.d.ts +0 -60
- package/vendor/tui/types/render/inspector.d.ts +0 -62
- package/vendor/tui/types/render/lines.d.ts +0 -86
- package/vendor/tui/types/render/markdown.d.ts +0 -29
- package/vendor/tui/types/render/projection.d.ts +0 -587
- package/vendor/tui/types/render/status.d.ts +0 -196
- package/vendor/tui/types/render/text.d.ts +0 -64
- package/vendor/tui/types/render/tool-detail.d.ts +0 -94
- package/vendor/tui/types/render/tool-preview.d.ts +0 -28
- package/vendor/tui/types/render/usage.d.ts +0 -113
- package/vendor/tui/types/render/width.d.ts +0 -29
- package/vendor/tui/types/session-directory.d.ts +0 -190
- package/vendor/tui/types/session-query.d.ts +0 -92
- package/vendor/tui/types/session-switch.d.ts +0 -17
- package/vendor/tui/types/settings-file.d.ts +0 -41
- package/vendor/tui/types/skills.d.ts +0 -47
- package/vendor/tui/types/startup.d.ts +0 -65
- package/vendor/tui/types/store.d.ts +0 -58
- package/vendor/tui/types/subagents.d.ts +0 -70
- package/vendor/tui/types/terminal-title.d.ts +0 -66
- package/vendor/tui/types/theme-panel.d.ts +0 -24
- package/vendor/tui/types/theme.d.ts +0 -434
- package/vendor/tui/types/update-panel.d.ts +0 -49
- package/vendor/tui/types/update.d.ts +0 -75
- package/vendor/tui/types/version.d.ts +0 -19
- package/vendor/tui/types/whale-glyph.d.ts +0 -6
- /package/vendor/tui/{dscode-clipboard-image → lib/dscode/clipboard-image}/clipboard-image.swift +0 -0
- /package/vendor/tui/{dscode-clipboard-image → lib/dscode/clipboard-image}/index.mjs +0 -0
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal animation helpers derived from the web design language:
|
|
3
|
+
* thinking uses Codex's slow shimmer sweep, the busy composer marker uses the
|
|
4
|
+
* original braille chase, and the streaming caret blink is the Claude-Code
|
|
5
|
+
* convention.
|
|
6
|
+
*
|
|
7
|
+
* The DeepSeek model-switch easter egg ports Codex's effort-ignition "Wave"
|
|
8
|
+
* style (`codex-rs/tui/src/bottom_pane/effort_ignition_styles.rs`): switching
|
|
9
|
+
* INTO an official DeepSeek route sweeps a blue wave across the composer's
|
|
10
|
+
* input row — one column per cell, `backgroundColor` = the sampled wave
|
|
11
|
+
* color — then, on the deepseek (Ultra-equivalent) tier, drops the `· ✦ ✧`
|
|
12
|
+
* sparkle sequence into the rightmost blank cell before fading. The prompt
|
|
13
|
+
* marker keeps the tier accent afterwards (persistent, like Codex's prompt
|
|
14
|
+
* charge). Pure functions only — the Ink layer owns timers and colors.
|
|
15
|
+
*
|
|
16
|
+
* Wave and Pulse deliberately extend the Codex port after in-terminal
|
|
17
|
+
* testing: the per-row phase cascade was removed (Codex tints each column
|
|
18
|
+
* across the whole band), Wave became a WATER SURFACE — one continuous sine
|
|
19
|
+
* swell spanning the band, mirror-symmetric about the center column, its
|
|
20
|
+
* crests flowing outward from the center with a symmetric fade envelope
|
|
21
|
+
* (the deepseek tier adds one faster harmonic crossing it), painted with
|
|
22
|
+
* Aurora's recipe: wide soft gradients, mirrored second-hue mixing, and a
|
|
23
|
+
* low alpha cap — no hard core line — while Pulse became true
|
|
24
|
+
* two-dimensional, cell-aspect-corrected detonations: soft wide rings whose
|
|
25
|
+
* color grades across their width, expanding outward through a symmetric
|
|
26
|
+
* fade envelope and each trailing an echo ripple in the next blue. Aurora
|
|
27
|
+
* keeps Codex's geometry verbatim.
|
|
28
|
+
*
|
|
29
|
+
* @module @deepseek-ai/dsh-code/render/animations
|
|
30
|
+
*/
|
|
31
|
+
/** Cadence for the original busy braille chase (8 frames × 125ms = 1s). */
|
|
32
|
+
export const BUSY_CHASE_TICK_MS = 125;
|
|
33
|
+
/** Original terminal StateDot chase frames. */
|
|
34
|
+
export const BUSY_CHASE_FRAMES = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];
|
|
35
|
+
/** Chase frame for a monotonic tick. */
|
|
36
|
+
export function busyChaseFrame(tick) {
|
|
37
|
+
return BUSY_CHASE_FRAMES[tick % BUSY_CHASE_FRAMES.length] ?? BUSY_CHASE_FRAMES[0];
|
|
38
|
+
}
|
|
39
|
+
/** Clock cadence for the Codex-style Deep diving shimmer. */
|
|
40
|
+
export const DEEP_DIVING_SHIMMER_TICK_MS = 33;
|
|
41
|
+
/** Codex shimmer timing and geometry. */
|
|
42
|
+
export const DEEP_DIVING_SHIMMER_DURATION_MS = 2_000;
|
|
43
|
+
export const DEEP_DIVING_SHIMMER_PADDING = 10;
|
|
44
|
+
export const DEEP_DIVING_SHIMMER_HALF_WIDTH = 5;
|
|
45
|
+
export const DEEP_DIVING_SPARK_BREATH_DURATION_MS = 2_000;
|
|
46
|
+
/**
|
|
47
|
+
* Codex's 2-second shimmer sweep, expressed in terminal ticks. The sweep has
|
|
48
|
+
* ten virtual columns of padding on either side and a five-column cosine
|
|
49
|
+
* highlight band, so the text changes gently rather than cycling rapidly.
|
|
50
|
+
*/
|
|
51
|
+
export function deepDivingShimmerIntensity(index, tick, graphemeCount) {
|
|
52
|
+
if (graphemeCount <= 0)
|
|
53
|
+
return 0;
|
|
54
|
+
const period = graphemeCount + DEEP_DIVING_SHIMMER_PADDING * 2;
|
|
55
|
+
const elapsed = ((tick * DEEP_DIVING_SHIMMER_TICK_MS) % DEEP_DIVING_SHIMMER_DURATION_MS + DEEP_DIVING_SHIMMER_DURATION_MS) % DEEP_DIVING_SHIMMER_DURATION_MS;
|
|
56
|
+
const position = elapsed / DEEP_DIVING_SHIMMER_DURATION_MS * period;
|
|
57
|
+
const distance = Math.abs(index + DEEP_DIVING_SHIMMER_PADDING - position);
|
|
58
|
+
if (distance > DEEP_DIVING_SHIMMER_HALF_WIDTH)
|
|
59
|
+
return 0;
|
|
60
|
+
const angle = Math.PI * distance / DEEP_DIVING_SHIMMER_HALF_WIDTH;
|
|
61
|
+
return 0.5 * (1 + Math.cos(angle));
|
|
62
|
+
}
|
|
63
|
+
/** Blue RGB color for one grapheme in the Codex-style shimmer. */
|
|
64
|
+
export function deepDivingGradientColor(index, tick, graphemeCount, base, highlight) {
|
|
65
|
+
return blendRgb(highlight, base, deepDivingShimmerIntensity(index, tick, graphemeCount));
|
|
66
|
+
}
|
|
67
|
+
/** Smooth breathing intensity for the always-visible Deep diving sparkle. */
|
|
68
|
+
export function deepDivingSparkIntensity(tick) {
|
|
69
|
+
const elapsed = ((tick * DEEP_DIVING_SHIMMER_TICK_MS) % DEEP_DIVING_SPARK_BREATH_DURATION_MS
|
|
70
|
+
+ DEEP_DIVING_SPARK_BREATH_DURATION_MS) % DEEP_DIVING_SPARK_BREATH_DURATION_MS;
|
|
71
|
+
const phase = elapsed / DEEP_DIVING_SPARK_BREATH_DURATION_MS;
|
|
72
|
+
return 0.2 + 0.8 * (0.5 + 0.5 * Math.cos(Math.PI * 2 * phase));
|
|
73
|
+
}
|
|
74
|
+
/** Blue RGB color for the breathing Deep diving sparkle. */
|
|
75
|
+
export function deepDivingSparkColor(tick, base, highlight) {
|
|
76
|
+
return blendRgb(highlight, base, deepDivingSparkIntensity(tick));
|
|
77
|
+
}
|
|
78
|
+
/** One full prismatic flow lap — lively: violet → fuchsia → cyan → violet in 2.4s. */
|
|
79
|
+
export const FLOW_PERIOD_MS = 2_400;
|
|
80
|
+
/**
|
|
81
|
+
* The prismatic flow color at one elapsed-time sample: a smoothstep walk
|
|
82
|
+
* around the anchor triangle, one full lap per {@link FLOW_PERIOD_MS}. Pure —
|
|
83
|
+
* the Ink layer owns the timer and passes its tick scaled by its own cadence
|
|
84
|
+
* (tick × tickMs). Callers gate on the animations preference and fall back to
|
|
85
|
+
* a static palette color when animation is off.
|
|
86
|
+
* @param elapsedMs - milliseconds since the flow started (any sign or size).
|
|
87
|
+
* @param anchors - the colors to walk, in order (theme.ts FLOW_ANCHORS).
|
|
88
|
+
* @returns the interpolated anchor color at this instant.
|
|
89
|
+
*/
|
|
90
|
+
export function flowColor(elapsedMs, anchors) {
|
|
91
|
+
if (anchors.length === 0)
|
|
92
|
+
throw new Error('flowColor needs at least one anchor');
|
|
93
|
+
if (anchors.length === 1)
|
|
94
|
+
return anchors[0];
|
|
95
|
+
const lap = ((elapsedMs % FLOW_PERIOD_MS) + FLOW_PERIOD_MS) % FLOW_PERIOD_MS;
|
|
96
|
+
const span = FLOW_PERIOD_MS / anchors.length;
|
|
97
|
+
const at = lap / span;
|
|
98
|
+
const index = Math.floor(at);
|
|
99
|
+
const phase = at - index;
|
|
100
|
+
const eased = phase * phase * (3 - 2 * phase);
|
|
101
|
+
return blendRgb(anchors[(index + 1) % anchors.length], anchors[index % anchors.length], eased);
|
|
102
|
+
}
|
|
103
|
+
/** Caret blink cadence: one blink step (on or off) per tick. */
|
|
104
|
+
export const CARET_BLINK_TICK_MS = 530;
|
|
105
|
+
/** Caret visibility: half the ticks on, half off (530ms blink). */
|
|
106
|
+
export function caretVisible(tick) {
|
|
107
|
+
return tick % 2 === 0;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The one-shot DeepSeek model-switch easter egg: when the status bar model
|
|
111
|
+
* label switches to an official DeepSeek route, the composer's input row
|
|
112
|
+
* plays Codex's effort-ignition "Wave" — a blue crest sweeping the content
|
|
113
|
+
* row column by column (background tint ≤ 0.55 under the draft), plus the
|
|
114
|
+
* Ultra-style `· ✦ ✧` sparkles on the deepseek tier — and the prompt marker
|
|
115
|
+
* keeps the tier accent afterwards. The Ink layer owns the timer and reads
|
|
116
|
+
* the ACTIVE palette anchors (`getPalette`); everything below is pure
|
|
117
|
+
* interpolation over the colors it is given.
|
|
118
|
+
*/
|
|
119
|
+
/** Frame cadence of the DeepSeek wave: Codex's IGNITION_FRAME_TICK (33ms ≈ 30fps). */
|
|
120
|
+
export const DEEPSEEK_WAVE_TICK_MS = 33;
|
|
121
|
+
/** All styles in canonical order, for random selection. */
|
|
122
|
+
const DEEPSEEK_WAVE_STYLES = ['wave', 'aurora', 'pulse'];
|
|
123
|
+
/**
|
|
124
|
+
* The water surface: ONE continuous sine line spanning the whole band,
|
|
125
|
+
* mirror-symmetric about the center column, its crests flowing OUTWARD from
|
|
126
|
+
* the center (phase k·|x − center| − ω·t). No sweep window, no return trip —
|
|
127
|
+
* the surface fades in, flows, and fades out, symmetric in both space and
|
|
128
|
+
* time. The deepseek tier adds one faster, finer HARMONIC line whose crests
|
|
129
|
+
* cross the fundamental's: interleaved richness with both lines still
|
|
130
|
+
* symmetric and still only ever flowing outward.
|
|
131
|
+
*/
|
|
132
|
+
export const WAVE_SURFACE_AMPLITUDE = 0.8;
|
|
133
|
+
export const WAVE_SURFACE_HARMONIC = 0.45;
|
|
134
|
+
export const WAVE_SURFACE_WAVELENGTH = 40;
|
|
135
|
+
export const WAVE_SURFACE_OMEGA = 9;
|
|
136
|
+
/** Vertical thickness in lane units — Aurora-wide: soft gradients, no hard edges. */
|
|
137
|
+
export const WAVE_SURFACE_THICKNESS = 1.2;
|
|
138
|
+
/**
|
|
139
|
+
* The mirrored second-hue profile: the space BELOW the surface carries a
|
|
140
|
+
* second blue at this strength, so color (not just brightness) varies
|
|
141
|
+
* continuously across the wave — Aurora-style hue mixing instead of a
|
|
142
|
+
* single flat tint.
|
|
143
|
+
*/
|
|
144
|
+
export const WAVE_SURFACE_MIRROR = 0.6;
|
|
145
|
+
/** Aurora-style soft alpha: low gain, capped well under the pulse ring's. */
|
|
146
|
+
export const WAVE_SURFACE_ALPHA_GAIN = 0.45;
|
|
147
|
+
export const WAVE_SURFACE_ALPHA_CAP = 0.68;
|
|
148
|
+
/**
|
|
149
|
+
* Pulse ring geometry, softened to the Wave standard: a WIDE band
|
|
150
|
+
* (half-width 5.5) with a moderate peak riding the radius — all inside the
|
|
151
|
+
* hue blend, no hard white line — and an inner profile one hue over at a
|
|
152
|
+
* slightly smaller radius, so the ring's color grades continuously across
|
|
153
|
+
* its width (the radial analog of the water surface's mirrored hues).
|
|
154
|
+
*/
|
|
155
|
+
const PULSE_HALF_WIDTH = 5.5;
|
|
156
|
+
const PULSE_PEAK_HALF_WIDTH = 1.8;
|
|
157
|
+
const PULSE_PEAK_GAIN = 0.35;
|
|
158
|
+
const PULSE_INNER_OFFSET = 2.5;
|
|
159
|
+
const PULSE_INNER_STRENGTH = 0.6;
|
|
160
|
+
/** The inner edge of each ring carries the tier's third blue. */
|
|
161
|
+
const PULSE_INNER_HUE = 2;
|
|
162
|
+
/**
|
|
163
|
+
* The trailing echo ripple: every pulse ring drags a second, weaker ring at
|
|
164
|
+
* a fraction of its radius in the NEXT hue of the tier's blues, fading in a
|
|
165
|
+
* little after the primary so the center hole opens first.
|
|
166
|
+
*/
|
|
167
|
+
const PULSE_ECHO_RADIUS = 0.7;
|
|
168
|
+
const PULSE_ECHO_STRENGTH = 0.65;
|
|
169
|
+
const PULSE_ECHO_DELAY = 0.12;
|
|
170
|
+
/** Aurora-grade soft alpha for the detonation — a notch above the swell. */
|
|
171
|
+
export const PULSE_ALPHA_GAIN = 0.45;
|
|
172
|
+
export const PULSE_ALPHA_CAP = 0.72;
|
|
173
|
+
/**
|
|
174
|
+
* Terminal cell aspect (row height ÷ column width, ≈2.2 for common fonts).
|
|
175
|
+
* A ring computed in raw cell units looks vertically squashed; weighting row
|
|
176
|
+
* distance by the aspect makes the Pulse ring appear circular on screen.
|
|
177
|
+
*/
|
|
178
|
+
const PULSE_ROW_ASPECT = 2.2;
|
|
179
|
+
/** Sparkle start and frame cadence — Codex SPARK_START / SPARK_FRAME. */
|
|
180
|
+
const SPARK_START_MS = 900;
|
|
181
|
+
const SPARK_FRAME_MS = 100;
|
|
182
|
+
/** Sparkle glyphs in frame order — Codex SPARK_GLYPHS (`· ✦ ✧`). */
|
|
183
|
+
export const SPARK_GLYPHS = ['·', '✦', '✧'];
|
|
184
|
+
export const DEEPSEEK_WAVE_BANDS = {
|
|
185
|
+
wave: {
|
|
186
|
+
// Wave-Max: one band sweeping 0.10s..0.85s.
|
|
187
|
+
flash: [[0.10, 0.75, 1.0]],
|
|
188
|
+
// Wave-Ultra: two offset bands for a richer crest.
|
|
189
|
+
deepseek: [[0.10, 0.70, 1.0], [0.35, 0.55, 1.0]],
|
|
190
|
+
// Into the Unknown reuses the Ultra parameters verbatim.
|
|
191
|
+
unknown: [[0.10, 0.70, 1.0], [0.35, 0.55, 1.0]],
|
|
192
|
+
},
|
|
193
|
+
aurora: {
|
|
194
|
+
// Aurora-Max: two drifting bands (hues 0 and 1).
|
|
195
|
+
flash: [[0.35, 0.15, 0.0], [-0.50, 0.60, 1.0]],
|
|
196
|
+
// Aurora-Ultra: a third band adds hue 2.
|
|
197
|
+
deepseek: [[0.35, 0.15, 0.0], [-0.50, 0.60, 1.0], [0.75, 0.35, 2.0]],
|
|
198
|
+
unknown: [[0.35, 0.15, 0.0], [-0.50, 0.60, 1.0], [0.75, 0.35, 2.0]],
|
|
199
|
+
},
|
|
200
|
+
pulse: {
|
|
201
|
+
// Pulse-Max: one expanding ring.
|
|
202
|
+
flash: [[0.10, 0.60, 1.0]],
|
|
203
|
+
// Pulse-Ultra: two rings (inner weaker, outer stronger).
|
|
204
|
+
deepseek: [[0.10, 0.55, 0.8], [0.45, 0.55, 1.1]],
|
|
205
|
+
unknown: [[0.10, 0.55, 0.8], [0.45, 0.55, 1.1]],
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
/** Extra display time applied to every Codex ignition style. */
|
|
209
|
+
const DEEPSEEK_WAVE_DURATION_EXTENSION_MS = 200;
|
|
210
|
+
/** Original Codex duration used as the animation's sampling timeline. */
|
|
211
|
+
function deepseekWaveBaseDuration(tier, style) {
|
|
212
|
+
// The unknown tier reuses the deepseek (pro) durations exactly.
|
|
213
|
+
const pro = tier === 'deepseek' || tier === 'unknown';
|
|
214
|
+
switch (style) {
|
|
215
|
+
case 'aurora': return pro ? 1600 : 1300;
|
|
216
|
+
case 'pulse': return pro ? 1250 : 900;
|
|
217
|
+
case 'wave': return pro ? 1300 : 1000;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Total visible duration: the Codex ignition duration plus 200ms so its motion
|
|
222
|
+
* remains readable in a busy terminal.
|
|
223
|
+
* @param tier - the active wave tier.
|
|
224
|
+
* @param style - the active ignition style.
|
|
225
|
+
* @returns the duration in milliseconds.
|
|
226
|
+
*/
|
|
227
|
+
export function deepseekWaveDuration(tier, style = 'wave') {
|
|
228
|
+
return deepseekWaveBaseDuration(tier, style) + DEEPSEEK_WAVE_DURATION_EXTENSION_MS;
|
|
229
|
+
}
|
|
230
|
+
/** Map the extended display timeline back onto the original Codex samples. */
|
|
231
|
+
function deepseekWaveSampleElapsedMs(tick, tier, style) {
|
|
232
|
+
const base = deepseekWaveBaseDuration(tier, style);
|
|
233
|
+
return tick * DEEPSEEK_WAVE_TICK_MS * base / deepseekWaveDuration(tier, style);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Pick one ignition style at random, never repeating the previous one —
|
|
237
|
+
* Codex `IgnitionStyle::random`. Falls back to the remaining styles.
|
|
238
|
+
* @param previous - the style of the last trigger, if any.
|
|
239
|
+
* @returns a style different from `previous`.
|
|
240
|
+
*/
|
|
241
|
+
export function deepseekWaveStyleRandom(previous) {
|
|
242
|
+
const candidates = DEEPSEEK_WAVE_STYLES.filter(style => style !== previous);
|
|
243
|
+
return candidates[Math.floor(Math.random() * candidates.length)] ?? 'wave';
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Tier for a `provider/model` label: a MODEL ID containing `flash` runs the
|
|
247
|
+
* single-band flash tier; everything else (pro/reasoner/chat) runs the
|
|
248
|
+
* dual-band deepseek tier. Mirrors Codex's Max→Ultra mapping. Only the model
|
|
249
|
+
* segment (after the `/`) is matched, so a provider whose name contains
|
|
250
|
+
* `flash` cannot flip an unrelated model onto the flash tier.
|
|
251
|
+
* @param model - the `provider/model` label of the applied model.
|
|
252
|
+
* @returns the wave tier for that model.
|
|
253
|
+
*/
|
|
254
|
+
export function deepseekWaveTier(model) {
|
|
255
|
+
const slash = model.indexOf('/');
|
|
256
|
+
const id = slash < 0 ? model : model.slice(slash + 1);
|
|
257
|
+
return id.toLowerCase().includes('flash') ? 'flash' : 'deepseek';
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Cosine window — Codex `crest`: 1 exactly under the wave center, 0 from
|
|
261
|
+
* one half-width away.
|
|
262
|
+
* @param distance - distance from the crest center in half-widths.
|
|
263
|
+
* @returns the crest strength in 0..1.
|
|
264
|
+
*/
|
|
265
|
+
export function crest(distance) {
|
|
266
|
+
if (distance >= 1)
|
|
267
|
+
return 0;
|
|
268
|
+
return 0.5 * (1 + Math.cos(Math.PI * distance));
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Cubic ease-in-out — Codex `ease_in_out`: flat at both ends, steepest in
|
|
272
|
+
* the middle, so the crest accelerates and eases instead of sliding linearly.
|
|
273
|
+
* @param progress - raw progress (clamped to 0..1).
|
|
274
|
+
* @returns the eased progress in 0..1.
|
|
275
|
+
*/
|
|
276
|
+
export function easeInOut(progress) {
|
|
277
|
+
const p = Math.min(1, Math.max(0, progress));
|
|
278
|
+
if (p < 0.5)
|
|
279
|
+
return 4 * p * p * p;
|
|
280
|
+
const inverse = -2 * p + 2;
|
|
281
|
+
return 1 - (inverse * inverse * inverse) / 2;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Fade-in/fade-out envelope — Codex `envelope`: linear ramp over `fadeIn`
|
|
285
|
+
* at the start and `fadeOut` at the end, plateau at 1 between, 0 outside the
|
|
286
|
+
* total. The Wave style keeps the envelope at 1 (Codex paints Wave without
|
|
287
|
+
* an envelope); exported for the Aurora-style fades and for tests.
|
|
288
|
+
* @param elapsed - seconds since the animation started.
|
|
289
|
+
* @param total - total duration in seconds.
|
|
290
|
+
* @param fadeIn - seconds of fade-in.
|
|
291
|
+
* @param fadeOut - seconds of fade-out.
|
|
292
|
+
* @returns the envelope value in 0..1.
|
|
293
|
+
*/
|
|
294
|
+
export function envelope(elapsed, total, fadeIn, fadeOut) {
|
|
295
|
+
if (elapsed <= 0 || elapsed >= total)
|
|
296
|
+
return 0;
|
|
297
|
+
const rise = elapsed / Math.max(fadeIn, Number.EPSILON);
|
|
298
|
+
const fall = (total - elapsed) / Math.max(fadeOut, Number.EPSILON);
|
|
299
|
+
return Math.min(Math.max(Math.min(rise, fall), 0), 1);
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* The /rainbow celebration on the three-row composer band: a FIXED
|
|
303
|
+
* seven-color spectrum (not the rolled palette) that slides across every
|
|
304
|
+
* row as one ribbon. Triggered when switching to rainbow or rerolling the
|
|
305
|
+
* seed; independent of RAINBOW_SEED so the burst always reads as a prism.
|
|
306
|
+
*/
|
|
307
|
+
export const RAINBOW_BURST_TICK_MS = 33;
|
|
308
|
+
export const RAINBOW_BURST_DURATION_MS = 1_800;
|
|
309
|
+
export const RAINBOW_BURST_HUES = [
|
|
310
|
+
[255, 59, 48],
|
|
311
|
+
[255, 149, 0],
|
|
312
|
+
[255, 204, 0],
|
|
313
|
+
[52, 199, 89],
|
|
314
|
+
[0, 199, 190],
|
|
315
|
+
[10, 132, 255],
|
|
316
|
+
[175, 82, 222],
|
|
317
|
+
];
|
|
318
|
+
/**
|
|
319
|
+
* Smoothstep in 0..1, then wrap-lerp around the seven burst hues.
|
|
320
|
+
* Position 0 is red, wrapping back toward red at 1. Shared by the
|
|
321
|
+
* composer burst and the static rainbow whale header.
|
|
322
|
+
*/
|
|
323
|
+
export function rainbowSpectrumHue(position) {
|
|
324
|
+
const hues = RAINBOW_BURST_HUES;
|
|
325
|
+
const x = ((position % 1) + 1) % 1;
|
|
326
|
+
const scaled = x * hues.length;
|
|
327
|
+
const index = Math.floor(scaled);
|
|
328
|
+
const t = scaled - index;
|
|
329
|
+
const eased = t * t * (3 - 2 * t);
|
|
330
|
+
const a = hues[index % hues.length];
|
|
331
|
+
const b = hues[(index + 1) % hues.length];
|
|
332
|
+
return [
|
|
333
|
+
Math.round(a[0] + (b[0] - a[0]) * eased),
|
|
334
|
+
Math.round(a[1] + (b[1] - a[1]) * eased),
|
|
335
|
+
Math.round(a[2] + (b[2] - a[2]) * eased),
|
|
336
|
+
];
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Background tint for one composer-band cell during the rainbow burst.
|
|
340
|
+
* Every row of a column shares the same hue (a solid ribbon); edge rows
|
|
341
|
+
* are slightly dimmer so the middle editor row reads as the crest. The
|
|
342
|
+
* spectrum slides ~1.2 widths over the burst, then fades to the band base.
|
|
343
|
+
* @param tick - frame index at {@link RAINBOW_BURST_TICK_MS}.
|
|
344
|
+
* @param column - band column (0..width-1).
|
|
345
|
+
* @param width - band width in columns.
|
|
346
|
+
* @param base - the theme's composerBand color to blend toward.
|
|
347
|
+
* @param row - band row (0..rows-1).
|
|
348
|
+
* @param rows - band height (composer is three rows: pad, editor, pad).
|
|
349
|
+
* @returns the blended RGB, or null after the burst (or at zero alpha).
|
|
350
|
+
*/
|
|
351
|
+
export function rainbowBurstColumnBg(tick, column, width, base, row = 0, rows = 1) {
|
|
352
|
+
const elapsed = tick * RAINBOW_BURST_TICK_MS / 1000;
|
|
353
|
+
const total = RAINBOW_BURST_DURATION_MS / 1000;
|
|
354
|
+
const fade = envelope(elapsed, total, 0.18, 0.45);
|
|
355
|
+
if (fade <= 0)
|
|
356
|
+
return null;
|
|
357
|
+
const span = Math.max(1, width - 1);
|
|
358
|
+
const slide = elapsed / total * 1.2;
|
|
359
|
+
const mixed = rainbowSpectrumHue(column / span + slide);
|
|
360
|
+
const edge = rows > 1 && (row === 0 || row === rows - 1);
|
|
361
|
+
const alpha = Math.min(0.72 * fade * (edge ? 0.78 : 1), 0.70);
|
|
362
|
+
if (alpha < 0.02)
|
|
363
|
+
return null;
|
|
364
|
+
return blendRgb(mixed, base, alpha);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* One band's contributions at a column — Codex `band_sample`, redesigned:
|
|
368
|
+
* Wave is a WATER SURFACE — one continuous sine line, mirror-symmetric
|
|
369
|
+
* about the center column, crests flowing outward from the center (band 1
|
|
370
|
+
* of the deepseek tier is a faster harmonic line crossing it). Pulse
|
|
371
|
+
* detonates in TWO dimensions: soft rings that keep expanding through a
|
|
372
|
+
* symmetric fade envelope, color grading across each ring's width, with a
|
|
373
|
+
* trailing echo ripple. Aurora matches Codex verbatim.
|
|
374
|
+
* @param style - the ignition style.
|
|
375
|
+
* @param band - the band triple (meaning depends on the style).
|
|
376
|
+
* @param elapsed - seconds since the animation started.
|
|
377
|
+
* @param column - column index in the content row (0..width-1).
|
|
378
|
+
* @param width - content-row width in columns.
|
|
379
|
+
* @param context - band geometry (row position, undulation flag, pulse span).
|
|
380
|
+
* @returns one or two `[hueIndex, strength, core]` contributions.
|
|
381
|
+
*/
|
|
382
|
+
function bandSample(style, band, elapsed, column, width, context) {
|
|
383
|
+
const [first, second, third] = band;
|
|
384
|
+
switch (style) {
|
|
385
|
+
case 'wave': {
|
|
386
|
+
// The water surface: one continuous sine line across the whole band,
|
|
387
|
+
// mirror-symmetric about the center column (distance d = |x − center|
|
|
388
|
+
// drives the phase), crests flowing OUTWARD from the center — one
|
|
389
|
+
// direction, never a return trip. The band table's (launch, travel)
|
|
390
|
+
// becomes the line's fade envelope: fade in at `launch`, full until
|
|
391
|
+
// `launch + travel`, then fade out to the end — symmetric in time.
|
|
392
|
+
const fadeIn = first;
|
|
393
|
+
const fadeOut = Math.max(0.05, context.total - (first + second));
|
|
394
|
+
const fade = envelope(elapsed, context.total, fadeIn, fadeOut);
|
|
395
|
+
if (fade <= 0.01)
|
|
396
|
+
return [[0, 0, 0]];
|
|
397
|
+
// Band 0 is the fundamental swell; band 1 (deepseek tier) is a faster,
|
|
398
|
+
// finer harmonic crossing the fundamental's crests — two continuous
|
|
399
|
+
// symmetric lines interleaving instead of one shuttling back and forth.
|
|
400
|
+
const harmonic = context.bandIndex % 2 === 1;
|
|
401
|
+
const wavelength = harmonic ? WAVE_SURFACE_WAVELENGTH / 1.5 : WAVE_SURFACE_WAVELENGTH;
|
|
402
|
+
const omega = (harmonic ? 1.5 : 1) * WAVE_SURFACE_OMEGA;
|
|
403
|
+
const amplitude = (harmonic ? WAVE_SURFACE_HARMONIC : 1) * WAVE_SURFACE_AMPLITUDE;
|
|
404
|
+
const d = Math.abs(column - (width - 1) / 2);
|
|
405
|
+
const surface = amplitude * Math.sin(Math.PI * 2 * d / wavelength - omega * elapsed + (harmonic ? Math.PI / 2 : 0));
|
|
406
|
+
const fromSurface = Math.abs(context.u - surface);
|
|
407
|
+
const vertical = context.undulating ? crest(fromSurface / WAVE_SURFACE_THICKNESS) : 1;
|
|
408
|
+
// Aurora-style hue mixing: the MIRRORED profile below the surface
|
|
409
|
+
// carries a second blue (the harmonic band carries the third), so the
|
|
410
|
+
// color distribution flows continuously with the wave instead of one
|
|
411
|
+
// flat tint broken by a hard core line.
|
|
412
|
+
const mirrorHue = harmonic ? 1 : 2;
|
|
413
|
+
const below = context.undulating ? crest(Math.abs(context.u + surface) / WAVE_SURFACE_THICKNESS) : vertical;
|
|
414
|
+
return [
|
|
415
|
+
[0, fade * vertical, 0],
|
|
416
|
+
[mirrorHue, fade * below * WAVE_SURFACE_MIRROR, 0],
|
|
417
|
+
];
|
|
418
|
+
}
|
|
419
|
+
case 'aurora': {
|
|
420
|
+
const center = (0.5 + 0.38 * Math.sin(Math.PI * 2 * (first * elapsed + second))) * width;
|
|
421
|
+
const halfWidth = Math.max(width * 0.22, 4);
|
|
422
|
+
return [[Math.trunc(third), crest(Math.abs(column - center) / halfWidth), 0]];
|
|
423
|
+
}
|
|
424
|
+
case 'pulse': {
|
|
425
|
+
// Symmetric lifetime: the band table's (launch, travel) becomes the
|
|
426
|
+
// fade envelope (fade in at launch, full until launch+travel, then
|
|
427
|
+
// fade to the end) while the radius keeps expanding THROUGH the fade —
|
|
428
|
+
// a shockwave that never hits a wall, it dissolves mid-flight.
|
|
429
|
+
const launch = first;
|
|
430
|
+
const travel = second;
|
|
431
|
+
const fadeOut = Math.max(0.05, context.total - (launch + travel));
|
|
432
|
+
const fade = envelope(elapsed, context.total, launch, fadeOut);
|
|
433
|
+
if (fade <= 0.01)
|
|
434
|
+
return [[0, 0, 0]];
|
|
435
|
+
const progress = (elapsed - launch) / travel;
|
|
436
|
+
const radius = (1 - (1 - progress) ** 3) * context.pulseSpan;
|
|
437
|
+
const decay = third * (1 - 0.35 * Math.min(Math.max(progress, 0), 1));
|
|
438
|
+
const distance = Math.hypot(column - width / 2, context.dy);
|
|
439
|
+
const fromRing = Math.abs(distance - radius);
|
|
440
|
+
// Wide soft band with a moderate peak riding the radius — all in the
|
|
441
|
+
// hue blend, no hard line — and an inner edge one hue over at a
|
|
442
|
+
// slightly smaller radius: the ring's color grades across its width.
|
|
443
|
+
const band = crest(fromRing / PULSE_HALF_WIDTH) + PULSE_PEAK_GAIN * crest(fromRing / PULSE_PEAK_HALF_WIDTH);
|
|
444
|
+
const inner = crest(Math.abs(distance - (radius - PULSE_INNER_OFFSET)) / PULSE_HALF_WIDTH);
|
|
445
|
+
// Trailing echo ripple in the next blue, fading in after the primary
|
|
446
|
+
// so the center hole opens first.
|
|
447
|
+
const echoGate = envelope(elapsed, context.total, launch + PULSE_ECHO_DELAY, fadeOut);
|
|
448
|
+
const echo = crest(Math.abs(distance - radius * PULSE_ECHO_RADIUS) / PULSE_HALF_WIDTH)
|
|
449
|
+
* PULSE_ECHO_STRENGTH * echoGate;
|
|
450
|
+
return [
|
|
451
|
+
[context.bandIndex, fade * band * decay, 0],
|
|
452
|
+
[PULSE_INNER_HUE, fade * inner * decay * PULSE_INNER_STRENGTH, 0],
|
|
453
|
+
[context.bandIndex + 1, fade * echo * decay, 0],
|
|
454
|
+
];
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
/** Linear RGB blend — Codex `blend`: `fg * alpha + bg * (1 - alpha)`. */
|
|
459
|
+
function blendRgb(fg, bg, alpha) {
|
|
460
|
+
return [
|
|
461
|
+
Math.round(fg[0] * alpha + bg[0] * (1 - alpha)),
|
|
462
|
+
Math.round(fg[1] * alpha + bg[1] * (1 - alpha)),
|
|
463
|
+
Math.round(fg[2] * alpha + bg[2] * (1 - alpha)),
|
|
464
|
+
];
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Aurora-only per-row phase share of the duration: its drifting bands reach
|
|
468
|
+
* the top row first and the bottom row last, sweeping down the band. 0.12
|
|
469
|
+
* keeps the bottom row's lag inside the 200ms duration extension. Wave and
|
|
470
|
+
* Pulse deliberately share one timeline (see `deepseekWaveColumnBg`).
|
|
471
|
+
*/
|
|
472
|
+
const DEEPSEEK_WAVE_ROW_PHASE = 0.12;
|
|
473
|
+
/**
|
|
474
|
+
* The background color for one composer-band column at a tick — Codex
|
|
475
|
+
* `paint_bands` + `Canvas::tint` for all three styles. Bands overlap with a
|
|
476
|
+
* max for Wave/Pulse and a SUM for Aurora (Codex differs by style), the
|
|
477
|
+
* weighted hues mix per column (Wave/Pulse always end on hue 0), the tint
|
|
478
|
+
* blends the mixed hue toward the blank-cell base at the style's alpha cap,
|
|
479
|
+
* and Aurora applies its own fade envelope. Returns `null` when the column
|
|
480
|
+
* should stay transparent, so the row returns to no `backgroundColor` on
|
|
481
|
+
* both ends. With `rows > 1`: Wave is a water surface — every column
|
|
482
|
+
* lights the row nearest the surface's current height, so the light reads
|
|
483
|
+
* as ONE continuous wavy line spanning the band, symmetric about the center
|
|
484
|
+
* column and flowing outward (a single-row band falls back to a flat glow);
|
|
485
|
+
* Pulse rings in two dimensions around the band's center cell with trailing
|
|
486
|
+
* echo ripples; only Aurora samples the timeline shifted by a per-row phase
|
|
487
|
+
* offset.
|
|
488
|
+
* @param tick - wave frame (0, 1, … at DEEPSEEK_WAVE_TICK_MS).
|
|
489
|
+
* @param column - column index in the content row (0..width-1).
|
|
490
|
+
* @param width - content-row width in columns.
|
|
491
|
+
* @param tier - the wave tier (flash = Max, deepseek = Ultra parameters).
|
|
492
|
+
* @param style - the ignition style.
|
|
493
|
+
* @param hues - the tier's three hues.
|
|
494
|
+
* @param base - the blank-cell base color the tint blends toward.
|
|
495
|
+
* @param row - row index in the band (0..rows-1; default 0 = old single-row).
|
|
496
|
+
* @param rows - band height in rows (default 1).
|
|
497
|
+
* @returns the blended RGB background, or null for transparent.
|
|
498
|
+
*/
|
|
499
|
+
export function deepseekWaveColumnBg(tick, column, width, tier, style, hues, base, row = 0, rows = 1) {
|
|
500
|
+
const total = deepseekWaveBaseDuration(tier, style) / 1000;
|
|
501
|
+
// Only Aurora keeps the per-row phase cascade (its drifting bands stagger
|
|
502
|
+
// nicely). Wave and Pulse sample ONE shared timeline — Codex paints each
|
|
503
|
+
// column across the whole band height (`tint_column`), and the cascade we
|
|
504
|
+
// added on top of the port tore Pulse into misaligned per-row rings and
|
|
505
|
+
// smeared Wave's crest into a diagonal.
|
|
506
|
+
const elapsed = deepseekWaveSampleElapsedMs(tick, tier, style) / 1000
|
|
507
|
+
- (style === 'aurora' ? (row - (rows - 1) / 2) * total * DEEPSEEK_WAVE_ROW_PHASE : 0);
|
|
508
|
+
const dy = style === 'pulse' ? (row - (rows - 1) / 2) * PULSE_ROW_ASPECT : 0;
|
|
509
|
+
const undulating = rows > 1;
|
|
510
|
+
const u = undulating ? (row - (rows - 1) / 2) / ((rows - 1) / 2) : 0;
|
|
511
|
+
// The span is EXACTLY the band's far corner: the ring front reaches the
|
|
512
|
+
// edges as the fade envelope closes, so the detonation stays visible for
|
|
513
|
+
// its whole lifetime instead of rushing off the band mid-flight (the old
|
|
514
|
+
// +2·half-width margin made the ring's second half invisible).
|
|
515
|
+
const pulseSpan = Math.hypot(width / 2, ((rows - 1) / 2) * PULSE_ROW_ASPECT);
|
|
516
|
+
const fade = style === 'aurora' ? envelope(elapsed, total, 0.25, 0.40) : 1;
|
|
517
|
+
const weights = [0, 0, 0];
|
|
518
|
+
let bandIndex = 0;
|
|
519
|
+
for (const band of DEEPSEEK_WAVE_BANDS[style][tier]) {
|
|
520
|
+
for (const [hue, strength] of bandSample(style, band, elapsed, column, width, { dy, u, undulating, pulseSpan, bandIndex, total })) {
|
|
521
|
+
weights[hue] = style === 'aurora' ? weights[hue] + strength : Math.max(weights[hue], strength);
|
|
522
|
+
}
|
|
523
|
+
bandIndex += 1;
|
|
524
|
+
}
|
|
525
|
+
const weight = weights[0] + weights[1] + weights[2];
|
|
526
|
+
if (weight <= 0.01)
|
|
527
|
+
return null;
|
|
528
|
+
let red = 0;
|
|
529
|
+
let green = 0;
|
|
530
|
+
let blue = 0;
|
|
531
|
+
for (let index = 0; index < 3; index += 1) {
|
|
532
|
+
red += weights[index] * hues[index][0];
|
|
533
|
+
green += weights[index] * hues[index][1];
|
|
534
|
+
blue += weights[index] * hues[index][2];
|
|
535
|
+
}
|
|
536
|
+
const mixed = [
|
|
537
|
+
Math.round(red / weight),
|
|
538
|
+
Math.round(green / weight),
|
|
539
|
+
Math.round(blue / weight),
|
|
540
|
+
];
|
|
541
|
+
// Alpha caps, all Aurora-grade now: Aurora ≤0.50, the swell ≤0.68, the
|
|
542
|
+
// detonation a notch above at ≤0.72 — soft gradients everywhere, no hard
|
|
543
|
+
// lines, the color work done by hue mixing instead of brightness spikes.
|
|
544
|
+
const alpha = style === 'aurora'
|
|
545
|
+
? Math.min(weight * 0.40, 0.50) * fade
|
|
546
|
+
: style === 'wave'
|
|
547
|
+
? Math.min(weight * WAVE_SURFACE_ALPHA_GAIN, WAVE_SURFACE_ALPHA_CAP)
|
|
548
|
+
: Math.min(weight * PULSE_ALPHA_GAIN, PULSE_ALPHA_CAP);
|
|
549
|
+
if (alpha < 0.02)
|
|
550
|
+
return null;
|
|
551
|
+
return blendRgb(mixed, base, alpha);
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* The sparkle glyph for a tick — Codex `spark_frame`, sampled on the same
|
|
555
|
+
* proportionally slowed DeepSeek Wave timeline as the composer background.
|
|
556
|
+
* The Ink layer still must skip occupied cells.
|
|
557
|
+
* @param tick - wave frame at DEEPSEEK_WAVE_TICK_MS.
|
|
558
|
+
* @returns the sparkle glyph, or null outside the stretched tail window.
|
|
559
|
+
*/
|
|
560
|
+
export function deepseekWaveSpark(tick) {
|
|
561
|
+
const elapsed = deepseekWaveSampleElapsedMs(tick, 'deepseek', 'wave');
|
|
562
|
+
if (elapsed < SPARK_START_MS)
|
|
563
|
+
return null;
|
|
564
|
+
const frame = Math.floor((elapsed - SPARK_START_MS) / SPARK_FRAME_MS);
|
|
565
|
+
return SPARK_GLYPHS[frame] ?? null;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Whether the `deepseek` wordmark rides the wave at this tick: it fades in
|
|
569
|
+
* shortly after the first crest launches and out before the wave settles,
|
|
570
|
+
* so the brand name surfaces through the sweep's middle. The Ink layer
|
|
571
|
+
* places it in the row's blank mid-section (never over real draft text).
|
|
572
|
+
* @param tick - wave frame at DEEPSEEK_WAVE_TICK_MS.
|
|
573
|
+
* @param tier - the wave tier.
|
|
574
|
+
* @returns true while the wordmark should be visible.
|
|
575
|
+
*/
|
|
576
|
+
export function deepseekWaveWordVisible(tick, tier, style = 'wave') {
|
|
577
|
+
const total = deepseekWaveBaseDuration(tier, style) / 1000;
|
|
578
|
+
const elapsed = deepseekWaveSampleElapsedMs(tick, tier, style) / 1000;
|
|
579
|
+
return envelope(elapsed, total, total * 0.2, total * 0.35) > 0.25;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* The per-character color for the `deepseek` wordmark: the tier's hues
|
|
583
|
+
* cycled per character (d→hue0, e→hue1, e→hue2, …), a brand-gradient text.
|
|
584
|
+
* @param index - character index in the wordmark.
|
|
585
|
+
* @param hues - the tier's three hues.
|
|
586
|
+
* @returns the hue for that character.
|
|
587
|
+
*/
|
|
588
|
+
export function deepseekWaveWordHue(index, hues) {
|
|
589
|
+
return hues[index % hues.length];
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* True when a `provider/model` status label addresses the official DeepSeek
|
|
593
|
+
* route: either segment contains `deepseek` (case-insensitive), covering the
|
|
594
|
+
* `deepseek-official` provider route and its `deepseek-*` model ids.
|
|
595
|
+
* @param label - the status bar model label (`provider/model`).
|
|
596
|
+
* @returns whether the label names an official DeepSeek model.
|
|
597
|
+
*/
|
|
598
|
+
export function isOfficialDeepSeekLabel(label) {
|
|
599
|
+
const slash = label.indexOf('/');
|
|
600
|
+
const provider = slash < 0 ? label : label.slice(0, slash);
|
|
601
|
+
const model = slash < 0 ? '' : label.slice(slash + 1);
|
|
602
|
+
return provider.toLowerCase().includes('deepseek') || model.toLowerCase().includes('deepseek');
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Known reasoning-effort ranks in ascending order. Effort ids are opaque
|
|
606
|
+
* adapter-owned strings, so the rank table covers the conventional names
|
|
607
|
+
* (off → low → medium → high → xhigh → max/ultra); an unrecognized id
|
|
608
|
+
* ranks as unknown (0), which never triggers the high-effort wave.
|
|
609
|
+
*/
|
|
610
|
+
const EFFORT_RANK = {
|
|
611
|
+
off: 0,
|
|
612
|
+
none: 0,
|
|
613
|
+
low: 1,
|
|
614
|
+
medium: 2,
|
|
615
|
+
med: 2,
|
|
616
|
+
high: 3,
|
|
617
|
+
xhigh: 4,
|
|
618
|
+
'x-high': 4,
|
|
619
|
+
'very-high': 4,
|
|
620
|
+
max: 5,
|
|
621
|
+
maximum: 5,
|
|
622
|
+
ultra: 5,
|
|
623
|
+
};
|
|
624
|
+
/**
|
|
625
|
+
* True when an effective reasoning effort is STRICTLY above `high` — the
|
|
626
|
+
* trigger gate for the "Into the Unknown" wave on non-DeepSeek routes.
|
|
627
|
+
* Absent efforts and unrecognized ids never qualify.
|
|
628
|
+
* @param effort - the effective reasoning-effort id ('' or undefined when none).
|
|
629
|
+
* @returns whether the effort ranks above high.
|
|
630
|
+
*/
|
|
631
|
+
export function effortAboveHigh(effort) {
|
|
632
|
+
if (effort === undefined || effort === '')
|
|
633
|
+
return false;
|
|
634
|
+
const rank = EFFORT_RANK[effort.trim().toLowerCase()];
|
|
635
|
+
return rank !== undefined && rank > 3;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Parse a persisted animations preference (`animations.json`): timed
|
|
639
|
+
* animations are on by default and only an explicit `false` disables them —
|
|
640
|
+
* a missing key, corrupt value, or absent file all mean enabled, so the
|
|
641
|
+
* /animation toggle degrades exactly like every other user preference.
|
|
642
|
+
* @param value - the raw parsed JSON value (expected boolean).
|
|
643
|
+
* @returns whether timed animations should run.
|
|
644
|
+
*/
|
|
645
|
+
export function parseAnimationsPref(value) {
|
|
646
|
+
return value !== false;
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* One parsed `/animation` argument: '' toggles, `on|true|1` enables,
|
|
650
|
+
* `off|false|0` disables (case-insensitive, surrounding whitespace ignored),
|
|
651
|
+
* and anything else is a usage error the caller surfaces. Kept pure so the
|
|
652
|
+
* command's entire decision table is unit-testable.
|
|
653
|
+
* @param argument - the raw text after `/animation`.
|
|
654
|
+
* @returns `{ enabled }`, `'toggle'`, or `'usage'`.
|
|
655
|
+
*/
|
|
656
|
+
export function parseAnimationsArgument(argument) {
|
|
657
|
+
const normalized = argument.trim().toLowerCase();
|
|
658
|
+
if (normalized === '')
|
|
659
|
+
return 'toggle';
|
|
660
|
+
if (normalized === 'on' || normalized === 'true' || normalized === '1')
|
|
661
|
+
return { enabled: true };
|
|
662
|
+
if (normalized === 'off' || normalized === 'false' || normalized === '0')
|
|
663
|
+
return { enabled: false };
|
|
664
|
+
return 'usage';
|
|
665
|
+
}
|