dsh-agent-crew-faces 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tushar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,37 @@
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/experimental/crew-faces/README.md
5
+ /:
6
+ en: 43ce1f2d12a7b7d0
7
+ zh: ab9097841f16ba51
8
+ /deepseek-ai-dsh-crew-faces:
9
+ en: 732171a999049e74
10
+ zh: 29add66683ac0239
11
+ /deepseek-ai-dsh-crew-faces/summary:
12
+ en: fa2c991f0ba420ff
13
+ zh: 5a79c7ffbefabe38
14
+ /deepseek-ai-dsh-crew-faces/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: b00dddf4c88f0a45
17
+ /deepseek-ai-dsh-crew-faces/use-this-package:
18
+ en: feb411e00685922d
19
+ zh: 4034878e1390eaf6
20
+ /deepseek-ai-dsh-crew-faces/understand-the-implementation:
21
+ en: 56a9346e3db0152d
22
+ zh: 7ee8a4d1ea13b803
23
+ /deepseek-ai-dsh-crew-faces/further-exploration:
24
+ en: 36d906ef9e0464bf
25
+ zh: 5a49119b97d2ec08
26
+ /deepseek-ai-dsh-crew-faces/model-experience:
27
+ en: ff60fda60100a1d0
28
+ zh: d3df0ed6f6166762
29
+ /deepseek-ai-dsh-crew-faces/model-experience/kv-cache-effect:
30
+ en: ca75c51c89c2c9b0
31
+ zh: 477b768cc9dcda1a
32
+ /deepseek-ai-dsh-crew-faces/known-limitations-and-deferred-work:
33
+ en: 9196cfc3605b9761
34
+ zh: 91af04bacf3585aa
35
+ /deepseek-ai-dsh-crew-faces/known-limitations-and-deferred-work/dev-note:
36
+ en: 47713bb5d8aac71f
37
+ zh: 68c55d987308da7b
package/README.md CHANGED
@@ -1,3 +1,113 @@
1
- # Temporary Holding Version
1
+ ---
2
+ description: "SVG faces for crew agents: one of four bodies, a colour and two blinking pill eyes, derived deterministically from the agent id."
3
+ kind: "package-reference"
4
+ ---
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ # dsh-agent-crew-faces
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ Crew surfaces show each agent as a small face. `CrewFace` draws it as SVG on a 100x100 viewBox: one of four bodies (orb, block, drop, bean) in one of six colours, with two pill eyes and a blink every few seconds. The agent id selects body, colour and blink phase, so an agent keeps its face across reloads and machines. The face has no background shape.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ The [crew-agents](../crew-agents/README.md) bundle mounts this client plugin. It provides `ctx.crewFaces`; a browser plugin injects `crewFaces` and draws a face with its `Face` component:
29
+
30
+ ```tsx
31
+ const { Face } = ctx.crewFaces
32
+
33
+ <Face agentId={agent.id} mood={agent.working ? 'working' : 'idle'} size={32} label={t('crew.face', { name: agent.name })} />
34
+ ```
35
+
36
+ | Prop | Default | Meaning |
37
+ |---|---|---|
38
+ | `agentId` | required | Stable agent id; selects body, colour and blink phase |
39
+ | `mood` | `idle` | `working` moves both eyes up and right |
40
+ | `design` | unset | `{ body, color }` replacing the body and colour derived from the id; the id still sets the blink phase |
41
+ | `size` | `40` | Width and height in CSS pixels |
42
+ | `label` | unset | Localized accessible name; without it the face is decorative and `aria-hidden` |
43
+ | `className` | unset | Class on the `svg` element |
44
+
45
+ `ctx.crewFaces.geometry(agentId, mood)`, also exported as `faceGeometry`, returns the same face as data (body, colour, eye colour, body parts, eyes and blink offset) for callers that draw it themselves or compare faces.
46
+
47
+ Plugins share the component through the service rather than importing it, because client bundles may not import another plugin's values.
48
+
49
+ -----
50
+
51
+ <a id="understand-the-implementation"></a>
52
+ ## Understand the implementation
53
+
54
+ <details>
55
+ <summary>Implementation internals — click to expand</summary>
56
+
57
+ A 32-bit FNV-1a hash of the id selects the body from its low bits, the colour from bits 8 and up, and the blink offset from bits 16 and up.
58
+
59
+ | Body | Parts | Rotation | Face circle (cx, cy, r) |
60
+ |---|---|---|---|
61
+ | orb | circle (50,52) r38 | none | (50, 52, 38) |
62
+ | block | circles r11 at (27,27) and (73,27), 68x62 rect at (16,24) rx16 | none | (50, 56, 32) |
63
+ | drop | path `M50 10C58 22 86 44 86 62A36 36 0 0 1 14 62C14 44 42 22 50 10Z` | -26° about (50,60) | (54, 58, 34) |
64
+ | bean | 54x80 rect at (24,12) rx27 | -16° about (51,52) | (52, 46, 30) |
65
+
66
+ The face circle positions the eyes and is never drawn. Eye 1 is centred at (cx+0.2r, cy-0.36r), 0.29r wide and 0.52r tall, rotated -20°. Eye 2 is centred at (cx+0.6r, cy-0.47r), 0.8 times eye 1's width and 0.76 times its height, rotated -22°. Each eye is a rect whose `rx` is half its width. The `working` mood moves both centres 0.06r right and 0.06r up.
67
+
68
+ Body colours are `#0B0B0C`, `#4D6BFE`, `#E8B4A0`, `#A8C7A1`, `#C9B6E4` and `#F2D16B`. They identify an agent, so they stay fixed across themes. Eyes are `#17181A`, or white on the `#0B0B0C` body.
69
+
70
+ The blink is a CSS-module animation on both eyes; each face sets `--crew-blink-delay` from its id. Each eye scales to 10% height for a moment of a 5-second cycle, phase-shifted per id. `prefers-reduced-motion: reduce` stops it.
71
+
72
+ | Source | Contents |
73
+ |---|---|
74
+ | [`src/geometry.ts`](src/geometry.ts) | Hash, bodies, colours and eye placement |
75
+ | [`src/client/CrewFace.tsx`](src/client/CrewFace.tsx) | SVG component |
76
+ | [`src/client/CrewFace.module.css`](src/client/CrewFace.module.css) | Blink |
77
+ | [`src/client/index.ts`](src/client/index.ts) | `ctx.crewFaces` service |
78
+ | [`tests/`](tests/) | Geometry and markup tests |
79
+
80
+ </details>
81
+
82
+ -----
83
+
84
+ <a id="further-exploration"></a>
85
+ ## Further Exploration
86
+
87
+ - [ui-primitives](../../client/ui-primitives/README.md) — the shared React atoms crew pages compose with this face.
88
+
89
+ <a id="model-experience"></a>
90
+ ## Model Experience
91
+
92
+ None, as the package is a browser-side face component that registers nothing model-facing.
93
+
94
+ #### KV Cache effect
95
+
96
+ None; this package neither assembles nor sends a provider request.
97
+
98
+ ## Known Limitations and Deferred Work
99
+
100
+ <a id="known-limitations-and-deferred-work"></a>
101
+
102
+ - Two agents can share a face: there are 24 body and colour combinations.
103
+ - Body colours do not follow the light or dark theme, so a near-black body has low contrast on a dark surface.
104
+
105
+ <a id="dev-note"></a>
106
+ ### Dev Note
107
+
108
+ <details>
109
+ <summary>Working context for maintainers — click to expand</summary>
110
+
111
+ The markup tests render with `react-dom/server` in the Node lane because the face has no interaction.
112
+
113
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,113 @@
1
+ ---
2
+ description: "Crew 智能体的 SVG 头像:四种身体之一、一种颜色和两只会眨的胶囊形眼睛,均由智能体 ID 确定性地推导。"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # dsh-agent-crew-faces
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ Crew 界面把每个智能体显示为一个小头像。`CrewFace` 在 100x100 的 viewBox 上以 SVG 绘制它:四种身体(orb、block、drop、bean)之一,六种颜色之一,两只胶囊形眼睛,每隔几秒眨一次眼。智能体 ID 决定身体、颜色和眨眼相位,因此同一智能体在重新加载和不同机器上保持相同头像。头像没有背景形状。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [实现原理](#understand-the-implementation)
18
+ - [延伸阅读](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与后续工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ [crew-agents](../crew-agents/README.zh.md) bundle 挂载本客户端插件。它提供 `ctx.crewFaces`;浏览器插件注入 `crewFaces`,并用其 `Face` 组件绘制头像:
29
+
30
+ ```tsx
31
+ const { Face } = ctx.crewFaces
32
+
33
+ <Face agentId={agent.id} mood={agent.working ? 'working' : 'idle'} size={32} label={t('crew.face', { name: agent.name })} />
34
+ ```
35
+
36
+ | 属性 | 默认值 | 含义 |
37
+ |---|---|---|
38
+ | `agentId` | 必填 | 稳定的智能体 ID;决定身体、颜色和眨眼相位 |
39
+ | `mood` | `idle` | `working` 使两只眼睛向右上方移动 |
40
+ | `design` | 未设置 | `{ body, color }`,替换由 ID 推导的身体和颜色;眨眼相位仍由 ID 决定 |
41
+ | `size` | `40` | 宽度和高度,单位为 CSS 像素 |
42
+ | `label` | 未设置 | 已本地化的无障碍名称;未提供时头像为装饰性并设置 `aria-hidden` |
43
+ | `className` | 未设置 | `svg` 元素上的类名 |
44
+
45
+ `ctx.crewFaces.geometry(agentId, mood)`(也以 `faceGeometry` 导出)以数据形式返回同一头像(身体、颜色、眼睛颜色、身体部件、眼睛和眨眼偏移),供自行绘制或比较头像的调用方使用。
46
+
47
+ 插件通过该服务共享组件而不是直接导入,因为客户端 bundle 不能导入其他插件的值。
48
+
49
+ -----
50
+
51
+ <a id="understand-the-implementation"></a>
52
+ ## 实现原理
53
+
54
+ <details>
55
+ <summary>实现细节——点击展开</summary>
56
+
57
+ ID 的 32 位 FNV-1a 哈希以低位选择身体,以第 8 位起的位选择颜色,以第 16 位起的位选择眨眼偏移。
58
+
59
+ | 身体 | 部件 | 旋转 | 脸部圆 (cx, cy, r) |
60
+ |---|---|---|---|
61
+ | orb | 圆 (50,52) r38 | 无 | (50, 52, 38) |
62
+ | block | (27,27) 和 (73,27) 处 r11 的圆,(16,24) 处 68x62、rx16 的矩形 | 无 | (50, 56, 32) |
63
+ | drop | 路径 `M50 10C58 22 86 44 86 62A36 36 0 0 1 14 62C14 44 42 22 50 10Z` | 绕 (50,60) 旋转 -26° | (54, 58, 34) |
64
+ | bean | (24,12) 处 54x80、rx27 的矩形 | 绕 (51,52) 旋转 -16° | (52, 46, 30) |
65
+
66
+ 脸部圆用于定位眼睛,从不绘制。眼睛 1 的中心位于 (cx+0.2r, cy-0.36r),宽 0.29r、高 0.52r,旋转 -20°。眼睛 2 的中心位于 (cx+0.6r, cy-0.47r),宽度为眼睛 1 的 0.8 倍、高度为其 0.76 倍,旋转 -22°。每只眼睛是 `rx` 等于其宽度一半的矩形。`working` 情绪使两个中心各向右、向上移动 0.06r。
67
+
68
+ 身体颜色为 `#0B0B0C`、`#4D6BFE`、`#E8B4A0`、`#A8C7A1`、`#C9B6E4` 和 `#F2D16B`。它们用于识别智能体,因此不随主题变化。眼睛为 `#17181A`,在 `#0B0B0C` 身体上为白色。
69
+
70
+ 眨眼是作用于两只眼睛的 CSS module 动画;每个头像根据 ID 设置 `--crew-blink-delay`。每只眼睛在 5 秒周期中短暂缩放到 10% 高度,相位按 ID 偏移。`prefers-reduced-motion: reduce` 会停止动画。
71
+
72
+ | 源文件 | 内容 |
73
+ |---|---|
74
+ | [`src/geometry.ts`](src/geometry.ts) | 哈希、身体、颜色和眼睛位置 |
75
+ | [`src/client/CrewFace.tsx`](src/client/CrewFace.tsx) | SVG 组件 |
76
+ | [`src/client/CrewFace.module.css`](src/client/CrewFace.module.css) | 眨眼 |
77
+ | [`src/client/index.ts`](src/client/index.ts) | `ctx.crewFaces` 服务 |
78
+ | [`tests/`](tests/) | 几何与标记测试 |
79
+
80
+ </details>
81
+
82
+ -----
83
+
84
+ <a id="further-exploration"></a>
85
+ ## 延伸阅读
86
+
87
+ - [ui-primitives](../../client/ui-primitives/README.zh.md) — crew 页面与本头像组合使用的共享 React 基础组件。
88
+
89
+ <a id="model-experience"></a>
90
+ ## 模型体验
91
+
92
+ 无,本包是浏览器端头像组件,不注册任何面向模型的内容。
93
+
94
+ #### KV Cache 影响
95
+
96
+ 无;本包既不组装也不发送模型提供方请求。
97
+
98
+ ## 已知限制与后续工作
99
+
100
+ <a id="known-limitations-and-deferred-work"></a>
101
+
102
+ - 两个智能体可能共用同一头像:身体与颜色共有 24 种组合。
103
+ - 身体颜色不随浅色或深色主题变化,因此近黑色身体在深色背景上对比度较低。
104
+
105
+ <a id="dev-note"></a>
106
+ ### 开发备注
107
+
108
+ <details>
109
+ <summary>维护者工作背景——点击展开</summary>
110
+
111
+ 由于头像没有交互,标记测试在 Node 通道中使用 `react-dom/server` 渲染。
112
+
113
+ </details>
package/lib/client.js ADDED
@@ -0,0 +1,274 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-agent-crew-faces",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ //#region lib/types/geometry.js
9
+ /** Deterministic face geometry for a crew agent on a 100x100 viewBox. */
10
+ /** Bodies in hash order. */
11
+ const CREW_BODIES = [
12
+ "orb",
13
+ "block",
14
+ "drop",
15
+ "bean"
16
+ ];
17
+ /** Body fills in hash order; `#0B0B0C` takes white eyes. */
18
+ const CREW_COLORS = [
19
+ "#0B0B0C",
20
+ "#4D6BFE",
21
+ "#E8B4A0",
22
+ "#A8C7A1",
23
+ "#C9B6E4",
24
+ "#F2D16B"
25
+ ];
26
+ const DARK_BODY = "#0B0B0C";
27
+ const DARK_EYE = "#17181A";
28
+ const LIGHT_EYE = "#FFFFFF";
29
+ /** Working-mood eye offset as a fraction of the face radius. */
30
+ const WORKING_SHIFT = .06;
31
+ const SHAPES = {
32
+ orb: { parts: [{
33
+ kind: "circle",
34
+ cx: 50,
35
+ cy: 52,
36
+ r: 38
37
+ }] },
38
+ block: { parts: [
39
+ {
40
+ kind: "circle",
41
+ cx: 27,
42
+ cy: 27,
43
+ r: 11
44
+ },
45
+ {
46
+ kind: "circle",
47
+ cx: 73,
48
+ cy: 27,
49
+ r: 11
50
+ },
51
+ {
52
+ kind: "rect",
53
+ x: 16,
54
+ y: 24,
55
+ width: 68,
56
+ height: 62,
57
+ rx: 16
58
+ }
59
+ ] },
60
+ drop: {
61
+ parts: [{
62
+ kind: "path",
63
+ d: "M50 10C58 22 86 44 86 62A36 36 0 0 1 14 62C14 44 42 22 50 10Z"
64
+ }],
65
+ rotate: [
66
+ -26,
67
+ 50,
68
+ 60
69
+ ]
70
+ },
71
+ bean: {
72
+ parts: [{
73
+ kind: "rect",
74
+ x: 24,
75
+ y: 12,
76
+ width: 54,
77
+ height: 80,
78
+ rx: 27
79
+ }],
80
+ rotate: [
81
+ -16,
82
+ 51,
83
+ 52
84
+ ]
85
+ }
86
+ };
87
+ /** Face circle `(cx, cy, r)` per body; the eyes are placed relative to it and it is never drawn. */
88
+ const FACE_CIRCLES = {
89
+ orb: [
90
+ 50,
91
+ 52,
92
+ 38
93
+ ],
94
+ block: [
95
+ 50,
96
+ 56,
97
+ 32
98
+ ],
99
+ drop: [
100
+ 54,
101
+ 58,
102
+ 34
103
+ ],
104
+ bean: [
105
+ 52,
106
+ 46,
107
+ 30
108
+ ]
109
+ };
110
+ /**
111
+ * 32-bit FNV-1a hash of a string.
112
+ * @param text - input, hashed by UTF-16 code unit.
113
+ * @returns unsigned 32-bit hash.
114
+ */
115
+ function hash(text) {
116
+ let value = 2166136261;
117
+ for (let index = 0; index < text.length; index++) {
118
+ value ^= text.charCodeAt(index);
119
+ value = Math.imul(value, 16777619);
120
+ }
121
+ return value >>> 0;
122
+ }
123
+ /**
124
+ * Place both eyes on a face circle.
125
+ * @param body - body whose face circle the eyes follow.
126
+ * @param mood - `working` shifts both eyes up and right.
127
+ * @returns the larger eye, then the smaller one.
128
+ */
129
+ function eyesFor(body, mood) {
130
+ const [cx, cy, r] = FACE_CIRCLES[body];
131
+ const shift = mood === "working" ? WORKING_SHIFT * r : 0;
132
+ const width = .29 * r;
133
+ const height = .52 * r;
134
+ const small = {
135
+ width: .8 * width,
136
+ height: .76 * height
137
+ };
138
+ return [{
139
+ cx: cx + .2 * r + shift,
140
+ cy: cy - .36 * r - shift,
141
+ width,
142
+ height,
143
+ rx: width / 2,
144
+ rotate: -20
145
+ }, {
146
+ cx: cx + .6 * r + shift,
147
+ cy: cy - .47 * r - shift,
148
+ ...small,
149
+ rx: small.width / 2,
150
+ rotate: -22
151
+ }];
152
+ }
153
+ /**
154
+ * Derive one agent's face from its id; the same id always yields the same body, colour and blink phase, and an
155
+ * explicit design replaces the derived body and colour.
156
+ * @param agentId - stable agent identifier.
157
+ * @param mood - current expression.
158
+ * @param design - body and colour to use instead of the derived ones.
159
+ * @returns the face to draw.
160
+ */
161
+ function faceGeometry(agentId, mood = "idle", design) {
162
+ const value = hash(agentId);
163
+ const body = design?.body ?? CREW_BODIES[value % CREW_BODIES.length] ?? "orb";
164
+ const color = design?.color ?? CREW_COLORS[(value >>> 8) % CREW_COLORS.length] ?? DARK_BODY;
165
+ return {
166
+ body,
167
+ color,
168
+ eyeColor: color === DARK_BODY ? LIGHT_EYE : DARK_EYE,
169
+ shape: SHAPES[body],
170
+ eyes: eyesFor(body, mood),
171
+ blinkDelayMs: (value >>> 16) % 4e3
172
+ };
173
+ }
174
+ //#endregion
175
+ //#region \0dsh-css:/Users/tushar/Developer/deepseek-harness-fork/packages/experimental/crew-faces/src/client/CrewFace.module.css.mjs
176
+ const css = ".PJWiWW_eye{transform-box:fill-box;transform-origin:50%;animation:PJWiWW_blink 5s var(--crew-blink-delay,0s) infinite}@keyframes PJWiWW_blink{0%,94%,to{transform:scaleY(1)}97%{transform:scaleY(.1)}}@media (prefers-reduced-motion:reduce){.PJWiWW_eye{animation:none}}";
177
+ const tagId = "dsh-agent-crew-faces/CrewFace.module.css";
178
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
179
+ const tag = document.createElement("style");
180
+ tag.dataset.plugin = "dsh-agent-crew-faces";
181
+ tag.dataset.pluginCss = tagId;
182
+ tag.textContent = css;
183
+ document.head.appendChild(tag);
184
+ }
185
+ var CrewFace_module_css_default = {
186
+ "blink": "PJWiWW_blink",
187
+ "eye": "PJWiWW_eye"
188
+ };
189
+ //#endregion
190
+ //#region lib/types/client/CrewFace.js
191
+ function Part({ part, fill }) {
192
+ switch (part.kind) {
193
+ case "circle": return (0, react_jsx_runtime.jsx)("circle", {
194
+ cx: part.cx,
195
+ cy: part.cy,
196
+ r: part.r,
197
+ fill
198
+ });
199
+ case "rect": return (0, react_jsx_runtime.jsx)("rect", {
200
+ x: part.x,
201
+ y: part.y,
202
+ width: part.width,
203
+ height: part.height,
204
+ rx: part.rx,
205
+ fill
206
+ });
207
+ case "path": return (0, react_jsx_runtime.jsx)("path", {
208
+ d: part.d,
209
+ fill
210
+ });
211
+ }
212
+ }
213
+ /**
214
+ * Draw an agent's face on a 100x100 viewBox: body, two pill eyes and a periodic blink, with no background shape.
215
+ * The blink stops under reduced motion.
216
+ * @param props - agent id, mood, optional design, size and optional accessible label.
217
+ * @returns the SVG element.
218
+ */
219
+ function CrewFace({ agentId, mood = "idle", design, size = 40, label, className }) {
220
+ const face = faceGeometry(agentId, mood, design);
221
+ const { shape } = face;
222
+ const bodyTransform = shape.rotate === void 0 ? void 0 : `rotate(${shape.rotate.join(" ")})`;
223
+ return (0, react_jsx_runtime.jsxs)("svg", {
224
+ viewBox: "0 0 100 100",
225
+ width: size,
226
+ height: size,
227
+ className,
228
+ style: { "--crew-blink-delay": `${-face.blinkDelayMs}ms` },
229
+ "data-body": face.body,
230
+ "data-mood": mood,
231
+ ...label === void 0 ? { "aria-hidden": true } : {
232
+ role: "img",
233
+ "aria-label": label
234
+ },
235
+ children: [(0, react_jsx_runtime.jsx)("g", {
236
+ transform: bodyTransform,
237
+ children: shape.parts.map((part, index) => (0, react_jsx_runtime.jsx)(Part, {
238
+ part,
239
+ fill: face.color
240
+ }, index))
241
+ }), face.eyes.map((eye, index) => (0, react_jsx_runtime.jsx)("g", {
242
+ transform: `rotate(${eye.rotate} ${eye.cx} ${eye.cy})`,
243
+ children: (0, react_jsx_runtime.jsx)("rect", {
244
+ className: CrewFace_module_css_default.eye,
245
+ x: eye.cx - eye.width / 2,
246
+ y: eye.cy - eye.height / 2,
247
+ width: eye.width,
248
+ height: eye.height,
249
+ rx: eye.rx,
250
+ fill: face.eyeColor
251
+ })
252
+ }, index))]
253
+ });
254
+ }
255
+ //#endregion
256
+ //#region lib/types/client/index.js
257
+ /**
258
+ * Provide `ctx.crewFaces`.
259
+ * @param ctx - Client plugin context.
260
+ */
261
+ function apply(ctx) {
262
+ ctx.reflect.provide("crewFaces", {
263
+ Face: CrewFace,
264
+ geometry: faceGeometry
265
+ });
266
+ }
267
+ //#endregion
268
+ exports.CrewFace = CrewFace;
269
+ exports.apply = apply;
270
+ return module.exports;
271
+ }
272
+ });
273
+
274
+ //# sourceMappingURL=client.js.map
package/lib/index.js ADDED
@@ -0,0 +1,172 @@
1
+ //#region lib/types/geometry.js
2
+ /** Deterministic face geometry for a crew agent on a 100x100 viewBox. */
3
+ /** Bodies in hash order. */
4
+ const CREW_BODIES = [
5
+ "orb",
6
+ "block",
7
+ "drop",
8
+ "bean"
9
+ ];
10
+ /** Body fills in hash order; `#0B0B0C` takes white eyes. */
11
+ const CREW_COLORS = [
12
+ "#0B0B0C",
13
+ "#4D6BFE",
14
+ "#E8B4A0",
15
+ "#A8C7A1",
16
+ "#C9B6E4",
17
+ "#F2D16B"
18
+ ];
19
+ const DARK_BODY = "#0B0B0C";
20
+ const DARK_EYE = "#17181A";
21
+ const LIGHT_EYE = "#FFFFFF";
22
+ /** Working-mood eye offset as a fraction of the face radius. */
23
+ const WORKING_SHIFT = .06;
24
+ const SHAPES = {
25
+ orb: { parts: [{
26
+ kind: "circle",
27
+ cx: 50,
28
+ cy: 52,
29
+ r: 38
30
+ }] },
31
+ block: { parts: [
32
+ {
33
+ kind: "circle",
34
+ cx: 27,
35
+ cy: 27,
36
+ r: 11
37
+ },
38
+ {
39
+ kind: "circle",
40
+ cx: 73,
41
+ cy: 27,
42
+ r: 11
43
+ },
44
+ {
45
+ kind: "rect",
46
+ x: 16,
47
+ y: 24,
48
+ width: 68,
49
+ height: 62,
50
+ rx: 16
51
+ }
52
+ ] },
53
+ drop: {
54
+ parts: [{
55
+ kind: "path",
56
+ d: "M50 10C58 22 86 44 86 62A36 36 0 0 1 14 62C14 44 42 22 50 10Z"
57
+ }],
58
+ rotate: [
59
+ -26,
60
+ 50,
61
+ 60
62
+ ]
63
+ },
64
+ bean: {
65
+ parts: [{
66
+ kind: "rect",
67
+ x: 24,
68
+ y: 12,
69
+ width: 54,
70
+ height: 80,
71
+ rx: 27
72
+ }],
73
+ rotate: [
74
+ -16,
75
+ 51,
76
+ 52
77
+ ]
78
+ }
79
+ };
80
+ /** Face circle `(cx, cy, r)` per body; the eyes are placed relative to it and it is never drawn. */
81
+ const FACE_CIRCLES = {
82
+ orb: [
83
+ 50,
84
+ 52,
85
+ 38
86
+ ],
87
+ block: [
88
+ 50,
89
+ 56,
90
+ 32
91
+ ],
92
+ drop: [
93
+ 54,
94
+ 58,
95
+ 34
96
+ ],
97
+ bean: [
98
+ 52,
99
+ 46,
100
+ 30
101
+ ]
102
+ };
103
+ /**
104
+ * 32-bit FNV-1a hash of a string.
105
+ * @param text - input, hashed by UTF-16 code unit.
106
+ * @returns unsigned 32-bit hash.
107
+ */
108
+ function hash(text) {
109
+ let value = 2166136261;
110
+ for (let index = 0; index < text.length; index++) {
111
+ value ^= text.charCodeAt(index);
112
+ value = Math.imul(value, 16777619);
113
+ }
114
+ return value >>> 0;
115
+ }
116
+ /**
117
+ * Place both eyes on a face circle.
118
+ * @param body - body whose face circle the eyes follow.
119
+ * @param mood - `working` shifts both eyes up and right.
120
+ * @returns the larger eye, then the smaller one.
121
+ */
122
+ function eyesFor(body, mood) {
123
+ const [cx, cy, r] = FACE_CIRCLES[body];
124
+ const shift = mood === "working" ? WORKING_SHIFT * r : 0;
125
+ const width = .29 * r;
126
+ const height = .52 * r;
127
+ const small = {
128
+ width: .8 * width,
129
+ height: .76 * height
130
+ };
131
+ return [{
132
+ cx: cx + .2 * r + shift,
133
+ cy: cy - .36 * r - shift,
134
+ width,
135
+ height,
136
+ rx: width / 2,
137
+ rotate: -20
138
+ }, {
139
+ cx: cx + .6 * r + shift,
140
+ cy: cy - .47 * r - shift,
141
+ ...small,
142
+ rx: small.width / 2,
143
+ rotate: -22
144
+ }];
145
+ }
146
+ /**
147
+ * Derive one agent's face from its id; the same id always yields the same body, colour and blink phase, and an
148
+ * explicit design replaces the derived body and colour.
149
+ * @param agentId - stable agent identifier.
150
+ * @param mood - current expression.
151
+ * @param design - body and colour to use instead of the derived ones.
152
+ * @returns the face to draw.
153
+ */
154
+ function faceGeometry(agentId, mood = "idle", design) {
155
+ const value = hash(agentId);
156
+ const body = design?.body ?? CREW_BODIES[value % CREW_BODIES.length] ?? "orb";
157
+ const color = design?.color ?? CREW_COLORS[(value >>> 8) % CREW_COLORS.length] ?? DARK_BODY;
158
+ return {
159
+ body,
160
+ color,
161
+ eyeColor: color === DARK_BODY ? LIGHT_EYE : DARK_EYE,
162
+ shape: SHAPES[body],
163
+ eyes: eyesFor(body, mood),
164
+ blinkDelayMs: (value >>> 16) % 4e3
165
+ };
166
+ }
167
+ //#endregion
168
+ //#region lib/types/index.js
169
+ /** Register the browser-only contribution without Host state. */
170
+ function apply() {}
171
+ //#endregion
172
+ export { CREW_BODIES, CREW_COLORS, FACE_CIRCLES, apply, eyesFor, faceGeometry };
@@ -0,0 +1,22 @@
1
+ import { type CrewMood, type FaceDesign } from '../geometry.ts';
2
+ /** Props of {@link CrewFace}. */
3
+ export interface CrewFaceProps {
4
+ /** Stable agent id; selects body, colour and blink phase. */
5
+ readonly agentId: string;
6
+ readonly mood?: CrewMood;
7
+ /** Body and colour to use instead of the ones derived from the id. */
8
+ readonly design?: FaceDesign;
9
+ /** Rendered width and height in CSS pixels. */
10
+ readonly size?: number;
11
+ /** Accessible name, already localized by the caller; without it the face is decorative and hidden from assistive technology. */
12
+ readonly label?: string;
13
+ readonly className?: string;
14
+ }
15
+ /**
16
+ * Draw an agent's face on a 100x100 viewBox: body, two pill eyes and a periodic blink, with no background shape.
17
+ * The blink stops under reduced motion.
18
+ * @param props - agent id, mood, optional design, size and optional accessible label.
19
+ * @returns the SVG element.
20
+ */
21
+ export declare function CrewFace({ agentId, mood, design, size, label, className }: CrewFaceProps): import("react").JSX.Element;
22
+ //# sourceMappingURL=CrewFace.d.ts.map
@@ -0,0 +1,31 @@
1
+ /** Provide crew faces to other browser plugins as a Cordis service. */
2
+ import type { ComponentType } from 'react';
3
+ import type { Context } from '@deepseek-ai/cordis';
4
+ import { type CrewMood, type FaceDesign, type FaceGeometry } from '../geometry.ts';
5
+ import { type CrewFaceProps } from './CrewFace.tsx';
6
+ export { CrewFace, type CrewFaceProps } from './CrewFace.tsx';
7
+ /** Face drawing shared with other crew plugins. */
8
+ export interface CrewFaces {
9
+ /** SVG face component. */
10
+ readonly Face: ComponentType<CrewFaceProps>;
11
+ /**
12
+ * The face as data.
13
+ * @param agentId - stable agent id.
14
+ * @param mood - current expression.
15
+ * @param design - body and colour to use instead of the derived ones.
16
+ * @returns body, colours, eyes and blink offset.
17
+ */
18
+ readonly geometry: (agentId: string, mood?: CrewMood, design?: FaceDesign) => FaceGeometry;
19
+ }
20
+ declare module '@deepseek-ai/cordis' {
21
+ interface Context {
22
+ /** Crew agent faces. */
23
+ crewFaces: CrewFaces;
24
+ }
25
+ }
26
+ /**
27
+ * Provide `ctx.crewFaces`.
28
+ * @param ctx - Client plugin context.
29
+ */
30
+ export declare function apply(ctx: Context): void;
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,76 @@
1
+ /** Deterministic face geometry for a crew agent on a 100x100 viewBox. */
2
+ /** Body silhouettes a face can take. */
3
+ export type CrewBody = 'orb' | 'block' | 'drop' | 'bean';
4
+ /** An explicit body and colour that replace the ones derived from the id. */
5
+ export interface FaceDesign {
6
+ readonly body: CrewBody;
7
+ /** Body fill; `#0B0B0C` takes white eyes. */
8
+ readonly color: string;
9
+ }
10
+ /** Face expression; `working` shifts both eyes up and right. */
11
+ export type CrewMood = 'idle' | 'working';
12
+ /** Bodies in hash order. */
13
+ export declare const CREW_BODIES: readonly CrewBody[];
14
+ /** Body fills in hash order; `#0B0B0C` takes white eyes. */
15
+ export declare const CREW_COLORS: readonly string[];
16
+ /** One SVG primitive of a body, in viewBox units. */
17
+ export type BodyPart = {
18
+ readonly kind: 'circle';
19
+ readonly cx: number;
20
+ readonly cy: number;
21
+ readonly r: number;
22
+ } | {
23
+ readonly kind: 'rect';
24
+ readonly x: number;
25
+ readonly y: number;
26
+ readonly width: number;
27
+ readonly height: number;
28
+ readonly rx: number;
29
+ } | {
30
+ readonly kind: 'path';
31
+ readonly d: string;
32
+ };
33
+ /** A body's parts and the rotation applied to all of them. */
34
+ export interface BodyShape {
35
+ readonly parts: readonly BodyPart[];
36
+ /** `[degrees, cx, cy]` for SVG `rotate()`, absent when upright. */
37
+ readonly rotate?: readonly [number, number, number];
38
+ }
39
+ /** One pill eye: a rect centred at (cx, cy) with `rx` equal to half its width, rotated about its centre. */
40
+ export interface Eye {
41
+ readonly cx: number;
42
+ readonly cy: number;
43
+ readonly width: number;
44
+ readonly height: number;
45
+ readonly rx: number;
46
+ readonly rotate: number;
47
+ }
48
+ /** Everything needed to draw one face. */
49
+ export interface FaceGeometry {
50
+ readonly body: CrewBody;
51
+ readonly color: string;
52
+ readonly eyeColor: string;
53
+ readonly shape: BodyShape;
54
+ readonly eyes: readonly [Eye, Eye];
55
+ /** Blink phase offset in milliseconds, so faces side by side do not blink together. */
56
+ readonly blinkDelayMs: number;
57
+ }
58
+ /** Face circle `(cx, cy, r)` per body; the eyes are placed relative to it and it is never drawn. */
59
+ export declare const FACE_CIRCLES: Readonly<Record<CrewBody, readonly [number, number, number]>>;
60
+ /**
61
+ * Place both eyes on a face circle.
62
+ * @param body - body whose face circle the eyes follow.
63
+ * @param mood - `working` shifts both eyes up and right.
64
+ * @returns the larger eye, then the smaller one.
65
+ */
66
+ export declare function eyesFor(body: CrewBody, mood: CrewMood): [Eye, Eye];
67
+ /**
68
+ * Derive one agent's face from its id; the same id always yields the same body, colour and blink phase, and an
69
+ * explicit design replaces the derived body and colour.
70
+ * @param agentId - stable agent identifier.
71
+ * @param mood - current expression.
72
+ * @param design - body and colour to use instead of the derived ones.
73
+ * @returns the face to draw.
74
+ */
75
+ export declare function faceGeometry(agentId: string, mood?: CrewMood, design?: FaceDesign): FaceGeometry;
76
+ //# sourceMappingURL=geometry.d.ts.map
@@ -0,0 +1,5 @@
1
+ /** Crew agent face geometry; the SVG component is provided to browser plugins as `ctx.crewFaces`. */
2
+ export { CREW_BODIES, CREW_COLORS, FACE_CIRCLES, eyesFor, faceGeometry, type BodyPart, type BodyShape, type CrewBody, type CrewMood, type Eye, type FaceDesign, type FaceGeometry, } from './geometry.ts';
3
+ /** Register the browser-only contribution without Host state. */
4
+ export declare function apply(): void;
5
+ //# sourceMappingURL=index.d.ts.map
package/locale/de.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Crew-Gesichter",
4
+ "description": "Zeichnet für jeden Crew-Agenten ein kleines Gesicht, abgeleitet von seiner ID, das andere Crew-Plugins anzeigen."
5
+ }
6
+ }
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Crew faces",
4
+ "description": "Draws a small face for each crew agent, chosen from its id, for other crew plugins to show."
5
+ }
6
+ }
package/locale/es.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Caras de la crew",
4
+ "description": "Dibuja una pequeña cara para cada agente de la crew, elegida a partir de su id, para que la muestren otros plugins de Crew."
5
+ }
6
+ }
package/locale/fr.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Visages de la crew",
4
+ "description": "Dessine un petit visage pour chaque agent de la crew, choisi à partir de son identifiant, que les autres plugins Crew affichent."
5
+ }
6
+ }
package/locale/ja.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "クルーの顔",
4
+ "description": "各クルーエージェントの ID から小さな顔を描き、他の Crew プラグインで表示できるようにします。"
5
+ }
6
+ }
package/locale/pt.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Rostos da crew",
4
+ "description": "Desenha um pequeno rosto para cada agente da crew, escolhido a partir do seu id, para os outros plugins Crew exibirem."
5
+ }
6
+ }
package/locale/ru.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Лица команды",
4
+ "description": "Рисует маленькое лицо для каждого агента команды по его идентификатору, чтобы его показывали другие плагины Crew."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Crew 头像",
4
+ "description": "根据每位 crew 智能体的 ID 绘制一个小头像,供其他 crew 插件显示。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,64 @@
1
1
  {
2
2
  "name": "dsh-agent-crew-faces",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "description": "SVG faces for crew agents, derived deterministically from the agent id",
4
+ "version": "0.1.0",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/tusharcora/dsh-agent-crew.git",
8
+ "directory": "packages/crew-faces"
9
+ },
10
+ "type": "module",
11
+ "main": "lib/index.js",
12
+ "types": "lib/types/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./lib/types/index.d.ts",
16
+ "default": "./lib/index.js"
17
+ },
18
+ "./src/*": "./src/*",
19
+ "./package.json": "./package.json",
20
+ "./client": {
21
+ "types": "./lib/types/client/index.d.ts",
22
+ "default": "./lib/client.js"
23
+ },
24
+ "./locale/*.json": "./locale/*.json"
25
+ },
26
+ "dsh": {
27
+ "client": {
28
+ "platform": "web",
29
+ "inject": []
30
+ }
31
+ },
32
+ "license": "MIT",
33
+ "peerDependencies": {
34
+ "@deepseek-ai/cordis": "~4.1.0"
35
+ },
36
+ "files": [
37
+ "locale/*.json",
38
+ "lib/index.js",
39
+ "lib/client.js",
40
+ "lib/types/**/*.d.ts",
41
+ "LICENSE"
42
+ ],
43
+ "peerDependenciesMeta": {
44
+ "@deepseek-ai/cordis": {
45
+ "optional": true
46
+ }
47
+ },
48
+ "keywords": [
49
+ "dsh",
50
+ "dsh-plugin",
51
+ "deepseek-harness",
52
+ "deepseek-harness-plugin",
53
+ "deepseek",
54
+ "dsh-agent-crew"
55
+ ],
56
+ "author": "Tushar (https://github.com/tusharcora)",
57
+ "homepage": "https://github.com/tusharcora/dsh-agent-crew#readme",
58
+ "bugs": {
59
+ "url": "https://github.com/tusharcora/dsh-agent-crew/issues"
60
+ },
61
+ "engines": {
62
+ "dsh": ">=0.2.2-rc.1 <0.3.0"
63
+ }
64
+ }