reviewflow 3.29.0 → 3.31.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/CHANGELOG.md +24 -0
- package/dist/dashboard/index.html +54 -26
- package/dist/dashboard/modules/emberAvatar.d.ts +134 -0
- package/dist/dashboard/modules/emberAvatar.d.ts.map +1 -0
- package/dist/dashboard/modules/emberAvatar.js +192 -0
- package/dist/dashboard/modules/emberAvatar.js.map +1 -0
- package/dist/dashboard/modules/emberAvatarRenderer.d.ts +42 -0
- package/dist/dashboard/modules/emberAvatarRenderer.d.ts.map +1 -0
- package/dist/dashboard/modules/emberAvatarRenderer.js +119 -0
- package/dist/dashboard/modules/emberAvatarRenderer.js.map +1 -0
- package/dist/dashboard/styles.css +183 -8
- package/dist/frameworks/claude/claudeInvoker.d.ts +7 -0
- package/dist/frameworks/claude/claudeInvoker.d.ts.map +1 -1
- package/dist/frameworks/claude/claudeInvoker.js +1 -1
- package/dist/frameworks/claude/claudeInvoker.js.map +1 -1
- package/dist/main/routes.d.ts.map +1 -1
- package/dist/main/routes.js +6 -16
- package/dist/main/routes.js.map +1 -1
- package/dist/modules/claude-invocation/entities/claudeSession/claudeSession.gateway.d.ts +1 -1
- package/dist/modules/claude-invocation/entities/claudeSession/claudeSession.gateway.d.ts.map +1 -1
- package/dist/modules/claude-invocation/entities/claudeSession/claudeSession.guard.d.ts +1 -1
- package/dist/modules/claude-invocation/entities/claudeSession/claudeSession.schema.d.ts +2 -0
- package/dist/modules/claude-invocation/entities/claudeSession/claudeSession.schema.d.ts.map +1 -1
- package/dist/modules/claude-invocation/entities/claudeSession/claudeSession.schema.js +1 -1
- package/dist/modules/claude-invocation/entities/claudeSession/claudeSession.schema.js.map +1 -1
- package/dist/modules/ember-chat/entities/emberAnswer/emberAnswerTransport.gateway.d.ts +24 -0
- package/dist/modules/ember-chat/entities/emberAnswer/emberAnswerTransport.gateway.d.ts.map +1 -0
- package/dist/modules/ember-chat/entities/emberAnswer/emberAnswerTransport.gateway.js +2 -0
- package/dist/modules/ember-chat/entities/emberAnswer/emberAnswerTransport.gateway.js.map +1 -0
- package/dist/modules/ember-chat/interface-adapters/controllers/http/emberChat.routes.d.ts +2 -3
- package/dist/modules/ember-chat/interface-adapters/controllers/http/emberChat.routes.d.ts.map +1 -1
- package/dist/modules/ember-chat/interface-adapters/controllers/http/emberChat.routes.js +6 -4
- package/dist/modules/ember-chat/interface-adapters/controllers/http/emberChat.routes.js.map +1 -1
- package/dist/modules/ember-chat/interface-adapters/gateways/emberAnswerTransport.claude.gateway.d.ts +46 -0
- package/dist/modules/ember-chat/interface-adapters/gateways/emberAnswerTransport.claude.gateway.d.ts.map +1 -0
- package/dist/modules/ember-chat/interface-adapters/gateways/emberAnswerTransport.claude.gateway.js +176 -0
- package/dist/modules/ember-chat/interface-adapters/gateways/emberAnswerTransport.claude.gateway.js.map +1 -0
- package/dist/modules/ember-chat/interface-adapters/gateways/emberStreamJson.parser.d.ts +2 -1
- package/dist/modules/ember-chat/interface-adapters/gateways/emberStreamJson.parser.d.ts.map +1 -1
- package/dist/modules/ember-chat/interface-adapters/gateways/emberStreamJson.parser.js +8 -2
- package/dist/modules/ember-chat/interface-adapters/gateways/emberStreamJson.parser.js.map +1 -1
- package/dist/modules/ember-chat/interface-adapters/presenters/emberStatus.presenter.d.ts +1 -1
- package/dist/modules/ember-chat/interface-adapters/presenters/emberStatus.presenter.d.ts.map +1 -1
- package/dist/modules/ember-chat/services/emberSystemPrompt.d.ts.map +1 -1
- package/dist/modules/ember-chat/services/emberSystemPrompt.js +47 -4
- package/dist/modules/ember-chat/services/emberSystemPrompt.js.map +1 -1
- package/dist/modules/ember-chat/usecases/askEmber/askEmber.usecase.d.ts +5 -4
- package/dist/modules/ember-chat/usecases/askEmber/askEmber.usecase.d.ts.map +1 -1
- package/dist/modules/ember-chat/usecases/askEmber/askEmber.usecase.js +61 -9
- package/dist/modules/ember-chat/usecases/askEmber/askEmber.usecase.js.map +1 -1
- package/dist/modules/ember-chat/usecases/askEmber/emberStream.d.ts +8 -0
- package/dist/modules/ember-chat/usecases/askEmber/emberStream.d.ts.map +1 -0
- package/dist/modules/ember-chat/usecases/askEmber/emberStream.js +2 -0
- package/dist/modules/ember-chat/usecases/askEmber/emberStream.js.map +1 -0
- package/dist/tests/acceptance/190-ember-live-answers-subscription.acceptance.test.d.ts +2 -0
- package/dist/tests/acceptance/190-ember-live-answers-subscription.acceptance.test.d.ts.map +1 -0
- package/dist/tests/acceptance/190-ember-live-answers-subscription.acceptance.test.js +158 -0
- package/dist/tests/acceptance/190-ember-live-answers-subscription.acceptance.test.js.map +1 -0
- package/dist/tests/stubs/emberAnswerTransport.stub.d.ts +21 -0
- package/dist/tests/stubs/emberAnswerTransport.stub.d.ts.map +1 -0
- package/dist/tests/stubs/emberAnswerTransport.stub.js +54 -0
- package/dist/tests/stubs/emberAnswerTransport.stub.js.map +1 -0
- package/dist/tests/units/dashboard/modules/emberAvatar.test.d.ts +2 -0
- package/dist/tests/units/dashboard/modules/emberAvatar.test.d.ts.map +1 -0
- package/dist/tests/units/dashboard/modules/emberAvatar.test.js +74 -0
- package/dist/tests/units/dashboard/modules/emberAvatar.test.js.map +1 -0
- package/dist/tests/units/dashboard/modules/emberAvatarRenderer.test.d.ts +2 -0
- package/dist/tests/units/dashboard/modules/emberAvatarRenderer.test.d.ts.map +1 -0
- package/dist/tests/units/dashboard/modules/emberAvatarRenderer.test.js +44 -0
- package/dist/tests/units/dashboard/modules/emberAvatarRenderer.test.js.map +1 -0
- package/dist/tests/units/modules/ember-chat/controllers/emberChat.routes.test.js +21 -14
- package/dist/tests/units/modules/ember-chat/controllers/emberChat.routes.test.js.map +1 -1
- package/dist/tests/units/modules/ember-chat/gateways/emberStreamJson.parser.test.js +13 -0
- package/dist/tests/units/modules/ember-chat/gateways/emberStreamJson.parser.test.js.map +1 -1
- package/dist/tests/units/modules/ember-chat/services/emberSystemPrompt.test.js +19 -0
- package/dist/tests/units/modules/ember-chat/services/emberSystemPrompt.test.js.map +1 -1
- package/dist/tests/units/modules/ember-chat/usecases/askEmber.usecase.test.js +53 -20
- package/dist/tests/units/modules/ember-chat/usecases/askEmber.usecase.test.js.map +1 -1
- package/package.json +1 -1
- package/dist/modules/ember-chat/entities/emberSession/emberSession.schema.d.ts +0 -7
- package/dist/modules/ember-chat/entities/emberSession/emberSession.schema.d.ts.map +0 -1
- package/dist/modules/ember-chat/entities/emberSession/emberSession.schema.js +0 -3
- package/dist/modules/ember-chat/entities/emberSession/emberSession.schema.js.map +0 -1
- package/dist/modules/ember-chat/entities/emberSession/emberSessionState.d.ts +0 -13
- package/dist/modules/ember-chat/entities/emberSession/emberSessionState.d.ts.map +0 -1
- package/dist/modules/ember-chat/entities/emberSession/emberSessionState.js +0 -35
- package/dist/modules/ember-chat/entities/emberSession/emberSessionState.js.map +0 -1
- package/dist/modules/ember-chat/entities/emberSession/emberSessionTransport.gateway.d.ts +0 -26
- package/dist/modules/ember-chat/entities/emberSession/emberSessionTransport.gateway.d.ts.map +0 -1
- package/dist/modules/ember-chat/entities/emberSession/emberSessionTransport.gateway.js +0 -2
- package/dist/modules/ember-chat/entities/emberSession/emberSessionTransport.gateway.js.map +0 -1
- package/dist/modules/ember-chat/interface-adapters/gateways/emberSessionTransport.claude.gateway.d.ts +0 -13
- package/dist/modules/ember-chat/interface-adapters/gateways/emberSessionTransport.claude.gateway.d.ts.map +0 -1
- package/dist/modules/ember-chat/interface-adapters/gateways/emberSessionTransport.claude.gateway.js +0 -130
- package/dist/modules/ember-chat/interface-adapters/gateways/emberSessionTransport.claude.gateway.js.map +0 -1
- package/dist/modules/ember-chat/usecases/emberSession/emberSessionRegistry.d.ts +0 -36
- package/dist/modules/ember-chat/usecases/emberSession/emberSessionRegistry.d.ts.map +0 -1
- package/dist/modules/ember-chat/usecases/emberSession/emberSessionRegistry.js +0 -59
- package/dist/modules/ember-chat/usecases/emberSession/emberSessionRegistry.js.map +0 -1
- package/dist/tests/acceptance/189-ember-readonly-review-chat.acceptance.test.d.ts +0 -2
- package/dist/tests/acceptance/189-ember-readonly-review-chat.acceptance.test.d.ts.map +0 -1
- package/dist/tests/acceptance/189-ember-readonly-review-chat.acceptance.test.js +0 -124
- package/dist/tests/acceptance/189-ember-readonly-review-chat.acceptance.test.js.map +0 -1
- package/dist/tests/stubs/emberSessionTransport.stub.d.ts +0 -18
- package/dist/tests/stubs/emberSessionTransport.stub.d.ts.map +0 -1
- package/dist/tests/stubs/emberSessionTransport.stub.js +0 -75
- package/dist/tests/stubs/emberSessionTransport.stub.js.map +0 -1
- package/dist/tests/units/modules/ember-chat/entities/emberSessionState.test.d.ts +0 -2
- package/dist/tests/units/modules/ember-chat/entities/emberSessionState.test.d.ts.map +0 -1
- package/dist/tests/units/modules/ember-chat/entities/emberSessionState.test.js +0 -42
- package/dist/tests/units/modules/ember-chat/entities/emberSessionState.test.js.map +0 -1
- package/dist/tests/units/modules/ember-chat/usecases/emberSessionRegistry.test.d.ts +0 -2
- package/dist/tests/units/modules/ember-chat/usecases/emberSessionRegistry.test.d.ts.map +0 -1
- package/dist/tests/units/modules/ember-chat/usecases/emberSessionRegistry.test.js +0 -88
- package/dist/tests/units/modules/ember-chat/usecases/emberSessionRegistry.test.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.31.0](https://github.com/DGouron/review-flow/compare/reviewflow-v3.30.0...reviewflow-v3.31.0) (2026-05-29)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
* **ember:** echo user message and clear/disable input during answer ([a831339](https://github.com/DGouron/review-flow/commit/a8313396c487b08ad74a520ebf87b7b0d041db10))
|
|
14
|
+
* **ember:** implement SPEC-190 live answers via --bg subscription ([3e59692](https://github.com/DGouron/review-flow/commit/3e59692242602bd9e94baf8e28601daea5b72396))
|
|
15
|
+
* **ember:** SPEC-190 live answers via Claude subscription + chat UX ([dd3c35d](https://github.com/DGouron/review-flow/commit/dd3c35dbb2cae75943a7b607ed735cba4d2936bb))
|
|
16
|
+
* **ember:** sticky header, markdown answers, typing/answered animations ([401f534](https://github.com/DGouron/review-flow/commit/401f5344319c6801ec72171ad8cecae57bbf4338))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
* **ember:** contain wide content (inline code, pre, tables) in chat bubble ([586b9b3](https://github.com/DGouron/review-flow/commit/586b9b3673362a5aefb0d3c75bb15679c6e4e762))
|
|
22
|
+
* **ember:** correct --bg transcript tail and done-detection (SPEC-190) ([8f71836](https://github.com/DGouron/review-flow/commit/8f718361b68aa9eb9a1800298690dd3bde30c127))
|
|
23
|
+
* **ember:** guard extractText against non-array message content (SPEC-190) ([af3d9e5](https://github.com/DGouron/review-flow/commit/af3d9e53f45318a67329051d4daad47bef86766f))
|
|
24
|
+
|
|
25
|
+
## [3.30.0](https://github.com/DGouron/review-flow/compare/reviewflow-v3.29.0...reviewflow-v3.30.0) (2026-05-28)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
* **dashboard:** SPEC-189 Ember flame wireframe avatar + sidebar layout ([#248](https://github.com/DGouron/review-flow/issues/248)) ([7e82a2f](https://github.com/DGouron/review-flow/commit/7e82a2fb385c8990558e3fd15e103b2e8bbd40e7))
|
|
31
|
+
|
|
8
32
|
## [3.29.0](https://github.com/DGouron/review-flow/compare/reviewflow-v3.28.0...reviewflow-v3.29.0) (2026-05-28)
|
|
9
33
|
|
|
10
34
|
|
|
@@ -101,27 +101,27 @@
|
|
|
101
101
|
|
|
102
102
|
<div class="dashboard-layout">
|
|
103
103
|
<aside class="dashboard-sidebar" aria-label="Project tools">
|
|
104
|
-
<
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
<
|
|
112
|
-
|
|
104
|
+
<div class="sidebar-tool-buttons">
|
|
105
|
+
<button type="button" id="open-settings-modal-btn" class="sidebar-settings-button" hidden>
|
|
106
|
+
<span class="sidebar-settings-button__prefix">// SETTINGS</span>
|
|
107
|
+
</button>
|
|
108
|
+
<button type="button" id="open-economics-sheet-btn" class="sidebar-settings-button" onclick="openEconomicsSheet()">
|
|
109
|
+
<span class="sidebar-settings-button__prefix">// ECONOMICS</span>
|
|
110
|
+
</button>
|
|
111
|
+
<button type="button" id="open-stats-sheet-btn" class="sidebar-settings-button" onclick="openStatsSheet()" disabled aria-disabled="true">
|
|
112
|
+
<span class="sidebar-settings-button__prefix">// STATS</span>
|
|
113
|
+
</button>
|
|
114
|
+
</div>
|
|
113
115
|
|
|
114
116
|
<span id="config-status" class="config-status hidden"></span>
|
|
115
117
|
|
|
116
|
-
<section id="worktree-section" aria-label="Worktree pool"></section>
|
|
117
|
-
|
|
118
118
|
<section id="ember-chat-panel" class="ember-chat-panel" aria-label="Ember">
|
|
119
119
|
<div class="ember-chat-panel__header">
|
|
120
120
|
<span class="ember-chat-panel__prefix">// EMBER</span>
|
|
121
|
-
<canvas id="ember-avatar" class="ember-chat-panel__avatar" width="
|
|
121
|
+
<canvas id="ember-avatar" class="ember-chat-panel__avatar" width="360" height="360" aria-hidden="true"></canvas>
|
|
122
122
|
</div>
|
|
123
|
-
<
|
|
124
|
-
<div id="ember-status" class="ember-chat-panel__status" role="status" aria-live="polite"></div>
|
|
123
|
+
<div id="ember-thread" class="ember-chat-panel__thread" role="log" aria-live="polite" aria-label="Conversation Ember"></div>
|
|
124
|
+
<div id="ember-status" class="ember-chat-panel__status visually-hidden" role="status" aria-live="polite"></div>
|
|
125
125
|
<button type="button" id="ember-retry" class="ember-chat-panel__retry" hidden>// RÉESSAYER</button>
|
|
126
126
|
<form id="ember-form" class="ember-chat-panel__form" autocomplete="off">
|
|
127
127
|
<label class="visually-hidden" for="ember-question">Posez une question à Ember</label>
|
|
@@ -133,6 +133,8 @@
|
|
|
133
133
|
/>
|
|
134
134
|
</form>
|
|
135
135
|
</section>
|
|
136
|
+
|
|
137
|
+
<section id="worktree-section" aria-label="Worktree pool"></section>
|
|
136
138
|
</aside>
|
|
137
139
|
|
|
138
140
|
<main class="dashboard-main">
|
|
@@ -449,7 +451,7 @@
|
|
|
449
451
|
renderHeaderCapacityBadgeHtml,
|
|
450
452
|
} from './modules/headerCapacityBadge.js';
|
|
451
453
|
import { connectEmberStream, shouldSendQuestion } from './modules/emberChat.js';
|
|
452
|
-
import {
|
|
454
|
+
import { mountEmberAvatar } from './modules/emberAvatarRenderer.js';
|
|
453
455
|
|
|
454
456
|
const API_URL = window.location.origin;
|
|
455
457
|
const WS_URL = `ws://${window.location.host}/ws`;
|
|
@@ -3864,30 +3866,56 @@
|
|
|
3864
3866
|
function initEmberChat() {
|
|
3865
3867
|
const form = document.getElementById('ember-form');
|
|
3866
3868
|
const input = document.getElementById('ember-question');
|
|
3867
|
-
const
|
|
3869
|
+
const thread = document.getElementById('ember-thread');
|
|
3868
3870
|
const status = document.getElementById('ember-status');
|
|
3869
3871
|
const retry = document.getElementById('ember-retry');
|
|
3870
3872
|
const canvas = document.getElementById('ember-avatar');
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3873
|
+
const panel = document.getElementById('ember-chat-panel');
|
|
3874
|
+
if (!form || !input || !thread || !status || !canvas) return;
|
|
3875
|
+
|
|
3876
|
+
const avatar = mountEmberAvatar({ canvas, initialState: 'idle' });
|
|
3877
|
+
|
|
3878
|
+
const appendBubble = (role, text) => {
|
|
3879
|
+
const bubble = document.createElement('div');
|
|
3880
|
+
bubble.className = 'ember-chat-panel__msg ember-chat-panel__msg--' + role;
|
|
3881
|
+
bubble.textContent = text;
|
|
3882
|
+
thread.appendChild(bubble);
|
|
3883
|
+
thread.scrollTop = thread.scrollHeight;
|
|
3884
|
+
return bubble;
|
|
3885
|
+
};
|
|
3874
3886
|
|
|
3875
|
-
|
|
3876
|
-
|
|
3887
|
+
let lastQuestion = '';
|
|
3888
|
+
const ask = async (questionText) => {
|
|
3889
|
+
const question = questionText ?? input.value;
|
|
3877
3890
|
if (!shouldSendQuestion(question)) {
|
|
3878
3891
|
input.focus();
|
|
3879
3892
|
return;
|
|
3880
3893
|
}
|
|
3881
|
-
|
|
3894
|
+
lastQuestion = question;
|
|
3895
|
+
panel?.classList.add('ember-chat-panel--active');
|
|
3882
3896
|
status.textContent = '';
|
|
3883
3897
|
retry.hidden = true;
|
|
3898
|
+
appendBubble('user', question);
|
|
3899
|
+
input.value = '';
|
|
3900
|
+
input.disabled = true;
|
|
3901
|
+
const answerBubble = appendBubble('ember', '');
|
|
3902
|
+
answerBubble.classList.add('is-generating');
|
|
3903
|
+
answerBubble.innerHTML = '<span class="ember-typing" aria-hidden="true"><i></i><i></i><i></i></span>';
|
|
3904
|
+
let errored = false;
|
|
3884
3905
|
await connectEmberStream({
|
|
3885
3906
|
question,
|
|
3886
|
-
onAnswer: (text) => {
|
|
3887
|
-
onAvatarState: (state) => avatar.setState(state),
|
|
3888
|
-
onAnnounce: (text) => {
|
|
3907
|
+
onAnswer: (text) => { answerBubble.innerHTML = markdownToHtml(text); thread.scrollTop = thread.scrollHeight; },
|
|
3908
|
+
onAvatarState: (state) => { avatar.setState(state); if (state === 'error') errored = true; },
|
|
3909
|
+
onAnnounce: (text) => {
|
|
3910
|
+
status.textContent = text;
|
|
3911
|
+
if (errored) answerBubble.innerHTML = markdownToHtml(text);
|
|
3912
|
+
},
|
|
3889
3913
|
onRetryVisible: (visible) => { retry.hidden = !visible; },
|
|
3890
3914
|
});
|
|
3915
|
+
answerBubble.classList.remove('is-generating');
|
|
3916
|
+
answerBubble.classList.add(errored ? 'is-error' : 'is-answered');
|
|
3917
|
+
thread.scrollTop = thread.scrollHeight;
|
|
3918
|
+
input.disabled = false;
|
|
3891
3919
|
input.focus();
|
|
3892
3920
|
};
|
|
3893
3921
|
|
|
@@ -3895,7 +3923,7 @@
|
|
|
3895
3923
|
event.preventDefault();
|
|
3896
3924
|
void ask();
|
|
3897
3925
|
});
|
|
3898
|
-
retry.addEventListener('click', () => { void ask(); });
|
|
3926
|
+
retry.addEventListener('click', () => { void ask(lastQuestion); });
|
|
3899
3927
|
}
|
|
3900
3928
|
|
|
3901
3929
|
// Boot animations & observers exactly once, regardless of readyState timing
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps an ember state to its stroke/animation parameters. Keeps the renderer
|
|
3
|
+
* humble: every per-state decision lives here, not in the canvas loop.
|
|
4
|
+
*
|
|
5
|
+
* @param {EmberState} state
|
|
6
|
+
* @returns {EmberVisual}
|
|
7
|
+
*/
|
|
8
|
+
export function emberStateToVisual(state: EmberState): EmberVisual;
|
|
9
|
+
/**
|
|
10
|
+
* Builds the flame wireframe as a surface of revolution: a single tip vertex
|
|
11
|
+
* (index 0) plus `rings` × `meridians` body vertices, joined by tip spokes,
|
|
12
|
+
* vertical meridian lines and horizontal ring loops. Pure and deterministic.
|
|
13
|
+
*
|
|
14
|
+
* @param {{ rings: number; meridians: number }} options
|
|
15
|
+
* @returns {{ vertices: Vertex[]; edges: Array<[number, number]> }}
|
|
16
|
+
*/
|
|
17
|
+
export function buildFlameWireframe(options: {
|
|
18
|
+
rings: number;
|
|
19
|
+
meridians: number;
|
|
20
|
+
}): {
|
|
21
|
+
vertices: Vertex[];
|
|
22
|
+
edges: Array<[number, number]>;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {Object} Projection
|
|
26
|
+
* @property {number} tilt Fixed X-axis tilt in radians.
|
|
27
|
+
* @property {number} distance Camera distance for the perspective divide.
|
|
28
|
+
* @property {number} scale Pixels per unit at the projection plane.
|
|
29
|
+
* @property {number} centerX Canvas-space x offset.
|
|
30
|
+
* @property {number} centerY Canvas-space y offset.
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Projects a flame vertex to a 2D canvas point: rotates around Y, leans the
|
|
34
|
+
* upper body sideways by `swayOffset` scaled by height (a candle flame bends at
|
|
35
|
+
* the tip, not the base), applies the fixed tilt, then a perspective divide.
|
|
36
|
+
* Pure and deterministic.
|
|
37
|
+
*
|
|
38
|
+
* @param {Vertex} vertex
|
|
39
|
+
* @param {number} rotationRadians
|
|
40
|
+
* @param {number} swayOffset
|
|
41
|
+
* @param {Projection} projection
|
|
42
|
+
* @returns {{ x: number; y: number }}
|
|
43
|
+
*/
|
|
44
|
+
export function projectFlameVertex(vertex: Vertex, rotationRadians: number, swayOffset: number, projection: Projection): {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
};
|
|
48
|
+
export function emberRadiusFactor(visual: any, time: any): number;
|
|
49
|
+
/**
|
|
50
|
+
* The horizontal lean of the flame tip at a given time. Pure — 0 at time 0,
|
|
51
|
+
* always within ± swayAmount.
|
|
52
|
+
*
|
|
53
|
+
* @param {EmberVisual} visual
|
|
54
|
+
* @param {number} time Milliseconds since the loop started.
|
|
55
|
+
* @returns {number}
|
|
56
|
+
*/
|
|
57
|
+
export function emberSwayOffset(visual: EmberVisual, time: number): number;
|
|
58
|
+
/**
|
|
59
|
+
* Dashboard module — Ember flame wireframe avatar (SPEC-189).
|
|
60
|
+
* Humble object: pure functions, no DOM, no global state. Holds the flame-shaped
|
|
61
|
+
* wireframe geometry and every per-state visual decision, so the
|
|
62
|
+
* requestAnimationFrame loop in emberAvatarRenderer.js carries no branching.
|
|
63
|
+
*
|
|
64
|
+
* Ember is a warm wireframe BRAISE, not the setup wizard's abstract icosahedron:
|
|
65
|
+
* a teardrop flame mesh (pointed tip, rounded base) stroked in amber, that leans
|
|
66
|
+
* and flickers like a live coal — livelier and warmer when spoken to.
|
|
67
|
+
*
|
|
68
|
+
* Visual DNA: "Agentic OS" — dark warm near-black + amber. See
|
|
69
|
+
* project_agentic_os_design_dna.md.
|
|
70
|
+
*/
|
|
71
|
+
/**
|
|
72
|
+
* @typedef {'idle' | 'working' | 'error'} EmberState
|
|
73
|
+
*/
|
|
74
|
+
/** @type {EmberState[]} */
|
|
75
|
+
export const EMBER_STATES: EmberState[];
|
|
76
|
+
/** Top of the flame (the tip) in model space. */
|
|
77
|
+
export const FLAME_TIP_Y: 1.5;
|
|
78
|
+
/** Bottom of the flame (the rounded base) in model space. */
|
|
79
|
+
export const FLAME_BASE_Y: -1.15;
|
|
80
|
+
export type Vertex = [number, number, number];
|
|
81
|
+
export type Projection = {
|
|
82
|
+
/**
|
|
83
|
+
* Fixed X-axis tilt in radians.
|
|
84
|
+
*/
|
|
85
|
+
tilt: number;
|
|
86
|
+
/**
|
|
87
|
+
* Camera distance for the perspective divide.
|
|
88
|
+
*/
|
|
89
|
+
distance: number;
|
|
90
|
+
/**
|
|
91
|
+
* Pixels per unit at the projection plane.
|
|
92
|
+
*/
|
|
93
|
+
scale: number;
|
|
94
|
+
/**
|
|
95
|
+
* Canvas-space x offset.
|
|
96
|
+
*/
|
|
97
|
+
centerX: number;
|
|
98
|
+
/**
|
|
99
|
+
* Canvas-space y offset.
|
|
100
|
+
*/
|
|
101
|
+
centerY: number;
|
|
102
|
+
};
|
|
103
|
+
export type EmberState = "idle" | "working" | "error";
|
|
104
|
+
export type EmberVisual = {
|
|
105
|
+
/**
|
|
106
|
+
* CSS custom-property name driving the warm stroke.
|
|
107
|
+
*/
|
|
108
|
+
color: string;
|
|
109
|
+
/**
|
|
110
|
+
* Stroke width in device pixels.
|
|
111
|
+
*/
|
|
112
|
+
lineWidth: number;
|
|
113
|
+
/**
|
|
114
|
+
* Radians per second of the slow Y rotation.
|
|
115
|
+
*/
|
|
116
|
+
rotationSpeed: number;
|
|
117
|
+
/**
|
|
118
|
+
* Radians per millisecond of the candle-lean sway.
|
|
119
|
+
*/
|
|
120
|
+
swaySpeed: number;
|
|
121
|
+
/**
|
|
122
|
+
* Horizontal lean amplitude at the flame tip.
|
|
123
|
+
*/
|
|
124
|
+
swayAmount: number;
|
|
125
|
+
/**
|
|
126
|
+
* Amplitude of the scale shimmer (the coal breathing).
|
|
127
|
+
*/
|
|
128
|
+
flicker: number;
|
|
129
|
+
/**
|
|
130
|
+
* Stroke shadow-blur in pixels (the warm halo around lines).
|
|
131
|
+
*/
|
|
132
|
+
glow: number;
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=emberAvatar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emberAvatar.d.ts","sourceRoot":"","sources":["../../../src/dashboard/modules/emberAvatar.js"],"names":[],"mappings":"AA4CA;;;;;;GAMG;AACH,0CAHW,UAAU,GACR,WAAW,CAIvB;AAmBD;;;;;;;GAOG;AACH,6CAHW;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;CAAE,CAmClE;AAED;;;;;;;GAOG;AAEH;;;;;;;;;;;GAWG;AACH,2CANW,MAAM,mBACN,MAAM,cACN,MAAM,cACN,UAAU,GACR;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAuBpC;AAgBD,kEAMC;AAED;;;;;;;GAOG;AACH,wCAJW,WAAW,QACX,MAAM,GACJ,MAAM,CAIlB;AA/LD;;;;;;;;;;;;GAYG;AAEH;;GAEG;AAEH,2BAA2B;AAC3B,2BADW,UAAU,EAAE,CACkC;AAEzD,iDAAiD;AACjD,0BAA2B,GAAG,CAAC;AAC/B,6DAA6D;AAC7D,2BAA4B,CAAC,IAAI,CAAC;qBAgCrB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;;;;;UA6DvB,MAAM;;;;cACN,MAAM;;;;WACN,MAAM;;;;aACN,MAAM;;;;aACN,MAAM;;yBA1GP,MAAM,GAAG,SAAS,GAAG,OAAO;;;;;WAa3B,MAAM;;;;eACN,MAAM;;;;mBACN,MAAM;;;;eACN,MAAM;;;;gBACN,MAAM;;;;aACN,MAAM;;;;UACN,MAAM"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard module — Ember flame wireframe avatar (SPEC-189).
|
|
3
|
+
* Humble object: pure functions, no DOM, no global state. Holds the flame-shaped
|
|
4
|
+
* wireframe geometry and every per-state visual decision, so the
|
|
5
|
+
* requestAnimationFrame loop in emberAvatarRenderer.js carries no branching.
|
|
6
|
+
*
|
|
7
|
+
* Ember is a warm wireframe BRAISE, not the setup wizard's abstract icosahedron:
|
|
8
|
+
* a teardrop flame mesh (pointed tip, rounded base) stroked in amber, that leans
|
|
9
|
+
* and flickers like a live coal — livelier and warmer when spoken to.
|
|
10
|
+
*
|
|
11
|
+
* Visual DNA: "Agentic OS" — dark warm near-black + amber. See
|
|
12
|
+
* project_agentic_os_design_dna.md.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {'idle' | 'working' | 'error'} EmberState
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** @type {EmberState[]} */
|
|
20
|
+
export const EMBER_STATES = ['idle', 'working', 'error'];
|
|
21
|
+
|
|
22
|
+
/** Top of the flame (the tip) in model space. */
|
|
23
|
+
export const FLAME_TIP_Y = 1.5;
|
|
24
|
+
/** Bottom of the flame (the rounded base) in model space. */
|
|
25
|
+
export const FLAME_BASE_Y = -1.15;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {Object} EmberVisual
|
|
29
|
+
* @property {string} color CSS custom-property name driving the warm stroke.
|
|
30
|
+
* @property {number} lineWidth Stroke width in device pixels.
|
|
31
|
+
* @property {number} rotationSpeed Radians per second of the slow Y rotation.
|
|
32
|
+
* @property {number} swaySpeed Radians per millisecond of the candle-lean sway.
|
|
33
|
+
* @property {number} swayAmount Horizontal lean amplitude at the flame tip.
|
|
34
|
+
* @property {number} flicker Amplitude of the scale shimmer (the coal breathing).
|
|
35
|
+
* @property {number} glow Stroke shadow-blur in pixels (the warm halo around lines).
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/** @type {Record<EmberState, EmberVisual>} */
|
|
39
|
+
const EMBER_VISUALS = {
|
|
40
|
+
idle: { color: '--accent', lineWidth: 1.5, rotationSpeed: 0.3, swaySpeed: 0.0011, swayAmount: 0.08, flicker: 0.025, glow: 10 },
|
|
41
|
+
working: { color: '--accent', lineWidth: 1.8, rotationSpeed: 0.6, swaySpeed: 0.0026, swayAmount: 0.22, flicker: 0.16, glow: 16 },
|
|
42
|
+
error: { color: '--danger', lineWidth: 2, rotationSpeed: 0.18, swaySpeed: 0.0018, swayAmount: 0.14, flicker: 0.22, glow: 12 },
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Maps an ember state to its stroke/animation parameters. Keeps the renderer
|
|
47
|
+
* humble: every per-state decision lives here, not in the canvas loop.
|
|
48
|
+
*
|
|
49
|
+
* @param {EmberState} state
|
|
50
|
+
* @returns {EmberVisual}
|
|
51
|
+
*/
|
|
52
|
+
export function emberStateToVisual(state) {
|
|
53
|
+
return EMBER_VISUALS[state];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {[number, number, number]} Vertex
|
|
58
|
+
*/
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The flame radius at a normalised height ringT ∈ [0,1] (0 near the tip, 1 at the
|
|
62
|
+
* base). A teardrop profile: pointed at the top, bulging in the lower third, a
|
|
63
|
+
* small rounded base — the silhouette that reads as a coal/flame rather than a
|
|
64
|
+
* ball.
|
|
65
|
+
*
|
|
66
|
+
* @param {number} ringT
|
|
67
|
+
* @returns {number}
|
|
68
|
+
*/
|
|
69
|
+
function flameRadius(ringT) {
|
|
70
|
+
return Math.sin(Math.PI * ringT ** 1.3) * 0.9 + 0.06;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Builds the flame wireframe as a surface of revolution: a single tip vertex
|
|
75
|
+
* (index 0) plus `rings` × `meridians` body vertices, joined by tip spokes,
|
|
76
|
+
* vertical meridian lines and horizontal ring loops. Pure and deterministic.
|
|
77
|
+
*
|
|
78
|
+
* @param {{ rings: number; meridians: number }} options
|
|
79
|
+
* @returns {{ vertices: Vertex[]; edges: Array<[number, number]> }}
|
|
80
|
+
*/
|
|
81
|
+
export function buildFlameWireframe(options) {
|
|
82
|
+
const { rings, meridians } = options;
|
|
83
|
+
/** @type {Vertex[]} */
|
|
84
|
+
const vertices = [[0, FLAME_TIP_Y, 0]];
|
|
85
|
+
const indexAt = (ring, meridian) => 1 + ring * meridians + meridian;
|
|
86
|
+
|
|
87
|
+
for (let ring = 0; ring < rings; ring += 1) {
|
|
88
|
+
const ringT = (ring + 1) / rings;
|
|
89
|
+
const y = FLAME_TIP_Y + (FLAME_BASE_Y - FLAME_TIP_Y) * ringT;
|
|
90
|
+
const radius = flameRadius(ringT);
|
|
91
|
+
for (let meridian = 0; meridian < meridians; meridian += 1) {
|
|
92
|
+
const angle = (meridian / meridians) * Math.PI * 2;
|
|
93
|
+
vertices.push([Math.cos(angle) * radius, y, Math.sin(angle) * radius]);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** @type {Array<[number, number]>} */
|
|
98
|
+
const edges = [];
|
|
99
|
+
for (let meridian = 0; meridian < meridians; meridian += 1) {
|
|
100
|
+
edges.push([0, indexAt(0, meridian)]);
|
|
101
|
+
}
|
|
102
|
+
for (let ring = 0; ring < rings - 1; ring += 1) {
|
|
103
|
+
for (let meridian = 0; meridian < meridians; meridian += 1) {
|
|
104
|
+
edges.push([indexAt(ring, meridian), indexAt(ring + 1, meridian)]);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (let ring = 0; ring < rings; ring += 1) {
|
|
108
|
+
for (let meridian = 0; meridian < meridians; meridian += 1) {
|
|
109
|
+
edges.push([indexAt(ring, meridian), indexAt(ring, (meridian + 1) % meridians)]);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { vertices, edges };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @typedef {Object} Projection
|
|
118
|
+
* @property {number} tilt Fixed X-axis tilt in radians.
|
|
119
|
+
* @property {number} distance Camera distance for the perspective divide.
|
|
120
|
+
* @property {number} scale Pixels per unit at the projection plane.
|
|
121
|
+
* @property {number} centerX Canvas-space x offset.
|
|
122
|
+
* @property {number} centerY Canvas-space y offset.
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Projects a flame vertex to a 2D canvas point: rotates around Y, leans the
|
|
127
|
+
* upper body sideways by `swayOffset` scaled by height (a candle flame bends at
|
|
128
|
+
* the tip, not the base), applies the fixed tilt, then a perspective divide.
|
|
129
|
+
* Pure and deterministic.
|
|
130
|
+
*
|
|
131
|
+
* @param {Vertex} vertex
|
|
132
|
+
* @param {number} rotationRadians
|
|
133
|
+
* @param {number} swayOffset
|
|
134
|
+
* @param {Projection} projection
|
|
135
|
+
* @returns {{ x: number; y: number }}
|
|
136
|
+
*/
|
|
137
|
+
export function projectFlameVertex(vertex, rotationRadians, swayOffset, projection) {
|
|
138
|
+
const [x, y, z] = vertex;
|
|
139
|
+
|
|
140
|
+
const cosY = Math.cos(rotationRadians);
|
|
141
|
+
const sinY = Math.sin(rotationRadians);
|
|
142
|
+
const rotatedX = x * cosY + z * sinY;
|
|
143
|
+
const rotatedZ = -x * sinY + z * cosY;
|
|
144
|
+
|
|
145
|
+
const heightFactor = (y - FLAME_BASE_Y) / (FLAME_TIP_Y - FLAME_BASE_Y);
|
|
146
|
+
const leanedX = rotatedX + swayOffset * heightFactor;
|
|
147
|
+
|
|
148
|
+
const cosTilt = Math.cos(projection.tilt);
|
|
149
|
+
const sinTilt = Math.sin(projection.tilt);
|
|
150
|
+
const tiltedY = y * cosTilt - rotatedZ * sinTilt;
|
|
151
|
+
const tiltedZ = y * sinTilt + rotatedZ * cosTilt;
|
|
152
|
+
|
|
153
|
+
const perspective = projection.scale / (tiltedZ + projection.distance);
|
|
154
|
+
return {
|
|
155
|
+
x: projection.centerX + leanedX * perspective,
|
|
156
|
+
y: projection.centerY - tiltedY * perspective,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The breathing multiplier applied to the flame scale at a given time: a slow
|
|
162
|
+
* pulse plus a faster two-tone shimmer that reads as flicker. Pure — 1 at time 0,
|
|
163
|
+
* always within 1 ± flicker.
|
|
164
|
+
*
|
|
165
|
+
* @param {EmberVisual} visual
|
|
166
|
+
* @param {number} time Milliseconds since the loop started.
|
|
167
|
+
* @returns {number}
|
|
168
|
+
*/
|
|
169
|
+
const SHIMMER_SLOW_FREQUENCY = 0.013;
|
|
170
|
+
const SHIMMER_FAST_FREQUENCY = 0.031;
|
|
171
|
+
const SHIMMER_SLOW_WEIGHT = 0.6;
|
|
172
|
+
const SHIMMER_FAST_WEIGHT = 0.4;
|
|
173
|
+
|
|
174
|
+
export function emberRadiusFactor(visual, time) {
|
|
175
|
+
const shimmer =
|
|
176
|
+
visual.flicker *
|
|
177
|
+
(SHIMMER_SLOW_WEIGHT * Math.sin(time * SHIMMER_SLOW_FREQUENCY) +
|
|
178
|
+
SHIMMER_FAST_WEIGHT * Math.sin(time * SHIMMER_FAST_FREQUENCY));
|
|
179
|
+
return 1 + shimmer;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* The horizontal lean of the flame tip at a given time. Pure — 0 at time 0,
|
|
184
|
+
* always within ± swayAmount.
|
|
185
|
+
*
|
|
186
|
+
* @param {EmberVisual} visual
|
|
187
|
+
* @param {number} time Milliseconds since the loop started.
|
|
188
|
+
* @returns {number}
|
|
189
|
+
*/
|
|
190
|
+
export function emberSwayOffset(visual, time) {
|
|
191
|
+
return visual.swayAmount * Math.sin(time * visual.swaySpeed);
|
|
192
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emberAvatar.js","sourceRoot":"","sources":["../../../src/dashboard/modules/emberAvatar.js"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The structural slice of a canvas the renderer depends on. A real
|
|
3
|
+
* HTMLCanvasElement satisfies it; tests pass a lightweight fake without casts.
|
|
4
|
+
*
|
|
5
|
+
* @typedef {Object} EmberCanvas
|
|
6
|
+
* @property {number} width
|
|
7
|
+
* @property {number} height
|
|
8
|
+
* @property {(contextId: '2d') => CanvasRenderingContext2D | null} getContext
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Mounts the animated flame wireframe avatar on a 2D canvas and returns its
|
|
12
|
+
* controls. The page calls setState() on each stream event and destroy() before
|
|
13
|
+
* any teardown so the rAF loop never leaks.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} options
|
|
16
|
+
* @param {EmberCanvas} options.canvas
|
|
17
|
+
* @param {import('./emberAvatar.js').EmberState} [options.initialState]
|
|
18
|
+
* @param {(callback: FrameRequestCallback) => number} [options.requestFrame]
|
|
19
|
+
* @param {(handle: number) => void} [options.cancelFrame]
|
|
20
|
+
* @param {() => number} [options.now]
|
|
21
|
+
* @returns {{ setState: (state: import('./emberAvatar.js').EmberState) => void; destroy: () => void }}
|
|
22
|
+
*/
|
|
23
|
+
export function mountEmberAvatar(options: {
|
|
24
|
+
canvas: EmberCanvas;
|
|
25
|
+
initialState?: import("./emberAvatar.js").EmberState | undefined;
|
|
26
|
+
requestFrame?: ((callback: FrameRequestCallback) => number) | undefined;
|
|
27
|
+
cancelFrame?: ((handle: number) => void) | undefined;
|
|
28
|
+
now?: (() => number) | undefined;
|
|
29
|
+
}): {
|
|
30
|
+
setState: (state: import("./emberAvatar.js").EmberState) => void;
|
|
31
|
+
destroy: () => void;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The structural slice of a canvas the renderer depends on. A real
|
|
35
|
+
* HTMLCanvasElement satisfies it; tests pass a lightweight fake without casts.
|
|
36
|
+
*/
|
|
37
|
+
export type EmberCanvas = {
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
getContext: (contextId: "2d") => CanvasRenderingContext2D | null;
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=emberAvatarRenderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emberAvatarRenderer.d.ts","sourceRoot":"","sources":["../../../src/dashboard/modules/emberAvatarRenderer.js"],"names":[],"mappings":"AA8BA;;;;;;;;GAQG;AAEH;;;;;;;;;;;;GAYG;AACH,0CAPG;IAA6B,MAAM,EAA3B,WAAW;IACqC,YAAY;IACP,YAAY,eAAtD,oBAAoB,KAAK,MAAM;IACP,WAAW,aAArC,MAAM,KAAK,IAAI;IACD,GAAG,UAApB,MAAM;CACpB,GAAU;IAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,kBAAkB,EAAE,UAAU,KAAK,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CAmErG;;;;;;WAnFa,MAAM;YACN,MAAM;gBACN,CAAC,SAAS,EAAE,IAAI,KAAK,wBAAwB,GAAG,IAAI"}
|