@korso/shepherd-ui 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,47 +1,47 @@
1
- # @korso/shepherd-ui
2
-
3
- The Shepherd dashboard: an **auth-agnostic** React app (Vite) that renders the
4
- workspace landscape (crew, territory, active/done work, chat) and issues operator
5
- actions against the hub. "Auth-agnostic" means the core holds no notion of who
6
- the user is or how they authenticated — it renders coordination state and issues
7
- operator actions, leaving authentication to the layer in front of it.
8
-
9
- ## Two build outputs
10
-
11
- One source tree, two Vite builds (`npm run build` runs both):
12
-
13
- - **`dist/lib`** (`build:lib`) — a component library for **Korso** to embed in
14
- the hosted product. Exposes `<Dashboard/>`, `createShepherdClient`, the React
15
- context (`ShepherdClientProvider` / `useShepherdClient`), and types via the `.`
16
- export. It is **token-blind**: auth lives in the BFF in front of it. The
17
- stylesheet is opt-in (`@korso/shepherd-ui/styles.css`), not auto-imported.
18
- - **`dist/selfhost`** (`build:app`) — a self-contained SPA the **hub serves**
19
- verbatim via `@fastify/static` (the hub resolves `../../ui/dist/selfhost/`).
20
- It bundles its own CSS and mounts the token-gated root.
21
-
22
- Both `dist/` outputs are gitignored; `npm run build` (root) and the hub Docker
23
- image regenerate them.
24
-
25
- ## Two mounts
26
-
27
- - **Self-host (the Hub).** The Hub serves `<SelfHostApp/>` (the `./selfhost`
28
- export, mounted by `src/main.tsx`). This is the **one** place a team token is
29
- handled — the named exception to the auth-agnostic core: it prompts for the
30
- `TEAM_TOKEN`, persists it, builds a same-origin client that sends
31
- `Authorization: Bearer <token>`, and re-gates on a 401. The token gate lives
32
- **only** here, never in the `.` export or `<Dashboard/>`.
33
- - **Hosted (Korso).** Korso imports `<Dashboard/>` + `createShepherdClient` from
34
- the `.` export and supplies its own auth layer (the BFF). No token gate.
35
-
36
- ## Development
37
-
38
- ```
39
- npm -w @korso/shepherd-ui run dev # Vite dev server for the self-host SPA
40
- ```
41
-
42
- Other scripts: `build` (both outputs), `build:lib`, `build:app`, `type-check`
43
- (`tsc --noEmit`), `test` (Vitest, jsdom).
44
-
45
- ## Phase note
46
-
47
- Korso-side consumption of the `dist/lib` library is **finalized in Phase 5**.
1
+ # @korso/shepherd-ui
2
+
3
+ The Shepherd dashboard: an **auth-agnostic** React app (Vite) that renders the
4
+ workspace landscape (crew, territory, active/done work, chat) and issues operator
5
+ actions against the hub. "Auth-agnostic" means the core holds no notion of who
6
+ the user is or how they authenticated — it renders coordination state and issues
7
+ operator actions, leaving authentication to the layer in front of it.
8
+
9
+ ## Two build outputs
10
+
11
+ One source tree, two Vite builds (`npm run build` runs both):
12
+
13
+ - **`dist/lib`** (`build:lib`) — a component library for **Korso** to embed in
14
+ the hosted product. Exposes `<Dashboard/>`, `createShepherdClient`, the React
15
+ context (`ShepherdClientProvider` / `useShepherdClient`), and types via the `.`
16
+ export. It is **token-blind**: auth lives in the BFF in front of it. The
17
+ stylesheet is opt-in (`@korso/shepherd-ui/styles.css`), not auto-imported.
18
+ - **`dist/selfhost`** (`build:app`) — a self-contained SPA the **hub serves**
19
+ verbatim via `@fastify/static` (the hub resolves `../../ui/dist/selfhost/`).
20
+ It bundles its own CSS and mounts the token-gated root.
21
+
22
+ Both `dist/` outputs are gitignored; `npm run build` (root) and the hub Docker
23
+ image regenerate them.
24
+
25
+ ## Two mounts
26
+
27
+ - **Self-host (the Hub).** The Hub serves `<SelfHostApp/>` (the `./selfhost`
28
+ export, mounted by `src/main.tsx`). This is the **one** place a team token is
29
+ handled — the named exception to the auth-agnostic core: it prompts for the
30
+ `TEAM_TOKEN`, persists it, builds a same-origin client that sends
31
+ `Authorization: Bearer <token>`, and re-gates on a 401. The token gate lives
32
+ **only** here, never in the `.` export or `<Dashboard/>`.
33
+ - **Hosted (Korso).** Korso imports `<Dashboard/>` + `createShepherdClient` from
34
+ the `.` export and supplies its own auth layer (the BFF). No token gate.
35
+
36
+ ## Development
37
+
38
+ ```
39
+ npm -w @korso/shepherd-ui run dev # Vite dev server for the self-host SPA
40
+ ```
41
+
42
+ Other scripts: `build` (both outputs), `build:lib`, `build:app`, `type-check`
43
+ (`tsc --noEmit`), `test` (Vitest, jsdom).
44
+
45
+ ## Phase note
46
+
47
+ Korso-side consumption of the `dist/lib` library is **finalized in Phase 5**.
@@ -1,182 +1,182 @@
1
- :root {
2
- --bg:#ece0c6; --ink:#322c20; --ink2:#6f6450; --ink3:#9c917a;
3
- --line:#d7c9ac; --line2:#e0d4b9; --live:#3d8b58; --drop:#bd4f3a;
4
- --chip:#e3d6ba; --terr:#7a6a48; --card:#f5ecd9;
5
- }
6
- * { box-sizing:border-box; }
7
- html,body { height:100%; }
8
- body {
9
- margin:0; background:var(--bg); color:var(--ink);
10
- font:14px/1.45 'Inter', ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
11
- -webkit-font-smoothing:antialiased;
12
- }
13
- .wrap { max-width:1100px; margin:0 auto; padding:40px 44px; }
14
-
15
- header { display:flex; align-items:center; gap:12px; padding-bottom:18px; border-bottom:1px solid var(--line); }
16
- /* Chat view: the card supplies its own border, so the header rule is redundant. */
17
- #board:has(#panel-chat:not([hidden])) header { border-bottom:0; }
18
- .brand { font-size:17px; font-weight:600; }
19
- .vitals { color:var(--ink3); font-size:12.5px; }
20
- .grow { flex:1; }
21
- .status { font-size:12px; color:var(--ink3); }
22
- .status--ok { color:var(--live); }
23
- .status--warn { color:var(--terr); }
24
- .status--error { color:var(--drop); }
25
- .freshness { font-size:12px; color:var(--ink3); }
26
-
27
- /* Repo selector (hidden when <2 repos; populated by app.js) */
28
- .repo { position:relative; }
29
- .repo-trig { display:inline-flex; align-items:center; gap:6px; font-size:13px;
30
- font-family:inherit; color:var(--ink); appearance:none;
31
- border:1px solid var(--line); border-radius:8px; padding:4px 10px;
32
- background:rgba(255,250,238,.4); cursor:pointer; }
33
- .repo-trig .slash { color:var(--ink3); }
34
- .repo-menu { position:absolute; top:36px; left:0; min-width:200px; background:var(--card);
35
- border:1px solid var(--line); border-radius:10px; padding:5px;
36
- box-shadow:0 8px 24px rgba(50,40,20,.14); z-index:5; }
37
- .repo-mi { display:flex; justify-content:space-between; gap:14px; padding:7px 9px;
38
- width:100%; text-align:left; border:0; appearance:none; font-family:inherit; color:inherit;
39
- background:transparent; border-radius:7px; font-size:13px; cursor:pointer; }
40
- .repo-mi:hover { background:var(--chip); }
41
- .repo-mi.on { background:var(--chip); font-weight:600; }
42
- .repo-mi .ct { font-size:11px; color:var(--ink3); }
43
- .repo-mi.all { border-top:1px solid var(--line2); margin-top:4px; padding-top:9px; color:var(--ink2); }
44
-
45
- /* Tabs */
46
- .tabs { display:flex; gap:4px; margin-left:auto; }
47
- .tab { font-size:13px; color:var(--ink2); padding:5px 11px; border:0; border-radius:7px;
48
- background:transparent; cursor:pointer; font:inherit; }
49
- .tab--active { background:var(--ink); color:var(--bg); }
50
-
51
- /* Crew strip */
52
- .crew { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin:26px 0 32px; }
53
- .person { display:inline-flex; align-items:center; gap:9px; padding:5px 13px 5px 5px;
54
- border:1px solid var(--line); border-radius:999px; background:var(--card); font-size:13px; }
55
- .person--idle { opacity:.55; }
56
- .avatar { width:24px; height:24px; border-radius:50%; display:grid; place-items:center;
57
- font-size:10px; font-weight:700; color:#fff; flex:none; }
58
- .person__name { font-weight:600; }
59
- .person__doing { color:var(--ink3); font-size:12px; }
60
-
61
- /* Board */
62
- .board { display:grid; grid-template-columns:1fr 1px 1fr; gap:0; }
63
- .board__rule { background:var(--line); }
64
- .col { padding:0 36px; } .col:first-child { padding-left:0; } .col:last-child { padding-right:0; }
65
- .colhead { display:flex; align-items:center; gap:8px; margin-bottom:6px; }
66
- .colhead h2 { font-size:12px; font-weight:600; letter-spacing:.03em; margin:0;
67
- text-transform:uppercase; color:var(--ink2); }
68
- .colhead .n { font-size:11px; color:var(--ink3); background:var(--chip);
69
- border-radius:999px; padding:1px 8px; }
70
-
71
- .task { padding:16px 0; border-bottom:1px solid var(--line2); }
72
- .task__r1 { display:flex; align-items:center; gap:8px; }
73
- .task__who { font-size:13.5px; font-weight:600; }
74
- .task__tag { font-size:11px; color:var(--ink3); }
75
- .task__repo { font-size:11px; color:var(--terr); background:rgba(122,106,72,.1);
76
- border-radius:4px; padding:0 6px; }
77
- .task__stat { margin-left:auto; font-size:11px; font-weight:500; color:var(--ink3); }
78
- .task__stat--drop { color:var(--drop); }
79
- .livedot { width:7px; height:7px; border-radius:50%; background:var(--live); margin-left:auto; }
80
- .task__intent { font-size:13.5px; margin:5px 0 0; line-height:1.45; }
81
- .terr { margin-top:8px; display:flex; flex-wrap:wrap; gap:6px; align-items:center; }
82
- .terr__lbl { font-size:9.5px; text-transform:uppercase; letter-spacing:.05em; color:var(--ink3); }
83
- .glob { font-family:ui-monospace, Menlo, monospace; font-size:11px; color:var(--terr);
84
- background:rgba(122,106,72,.12); border:1px solid rgba(122,106,72,.22);
85
- border-radius:5px; padding:1px 7px; }
86
- .task__meta { font-size:11.5px; color:var(--ink3); margin-top:6px; }
87
- /* expandable territory (2+ paths collapse to a one-line "N paths" toggle) */
88
- .terrx { margin-top:8px; }
89
- .terrx summary { list-style:none; cursor:pointer; display:flex; align-items:center;
90
- gap:6px; flex-wrap:wrap; user-select:none; }
91
- .terrx summary::-webkit-details-marker { display:none; }
92
- .terrx .terr--full { margin-top:8px; }
93
- .glob--more { cursor:pointer; color:var(--ink2); background:var(--chip);
94
- border:1px solid var(--line); }
95
- .glob--more .tw { color:var(--ink3); display:inline-block; transition:transform .15s; }
96
- .terrx[open] summary .glob--more .tw { transform:rotate(90deg); }
97
- /* grouped agent box: header + claims rail + collapsible "narrower" fold */
98
- .grp { padding:16px 0; border-bottom:1px solid var(--line2); }
99
- .grp__head { display:flex; align-items:center; gap:8px; }
100
- .grp__who { font-size:13.5px; font-weight:600; }
101
- .grp__tag { font-size:11px; color:var(--ink3); }
102
- .grp__count { font-size:11px; color:var(--ink3); }
103
- .grp__dot { width:7px; height:7px; border-radius:50%; background:var(--live); margin-left:auto; }
104
- .claims { margin:10px 0 0 6px; padding-left:14px; border-left:1px solid var(--line); }
105
- .claim { padding:6px 0; }
106
- .claim + .claim { border-top:1px solid var(--line2); margin-top:6px; }
107
- .fold { margin-top:10px; }
108
- .fold summary { list-style:none; cursor:pointer; font-size:12px; color:var(--ink2);
109
- display:inline-flex; align-items:center; gap:6px; user-select:none; }
110
- .fold summary::-webkit-details-marker { display:none; }
111
- .fold summary .tw { display:inline-block; color:var(--ink3); transition:transform .15s; }
112
- .fold[open] summary .tw { transform:rotate(90deg); }
113
- .fold__body { margin:8px 0 0 6px; padding-left:14px; border-left:1px dashed var(--line); }
114
- .covered { font-size:10.5px; color:var(--ink3); font-style:italic; margin-top:4px; }
115
- .day { font-size:11px; color:var(--ink3); padding:18px 0 2px; font-weight:600; }
116
- .more { margin-top:20px; font-size:12.5px; color:var(--ink2); padding:7px 13px;
117
- border:1px solid var(--line); border-radius:8px; background:rgba(255,250,238,.35);
118
- cursor:pointer; }
119
- .empty { color:var(--ink3); font-style:italic; padding:14px 0; }
120
-
121
- /* Chat (re-themed; structure unchanged) */
122
- .chat-wrap { background:var(--card); border:1px solid var(--line); border-radius:12px;
123
- display:flex; flex-direction:column; overflow:hidden; height:calc(100vh - 150px); min-height:320px; }
124
- .chat { flex:1; overflow-y:auto; padding:16px; }
125
- .chat { scrollbar-width:thin; scrollbar-color:var(--line) transparent; }
126
- .chat::-webkit-scrollbar { width:10px; }
127
- .chat::-webkit-scrollbar-track { background:transparent; }
128
- .chat::-webkit-scrollbar-thumb { background:var(--line); border-radius:999px;
129
- border:3px solid var(--card); background-clip:padding-box; }
130
- .chat::-webkit-scrollbar-thumb:hover { background:var(--ink3); background-clip:padding-box; }
131
- .chat__note { padding:9px 16px; font-size:11.5px; color:var(--ink3); }
132
- .msg { display:flex; gap:11px; padding:9px 0; }
133
- .msg__avatar { flex:none; width:32px; height:32px; border-radius:50%; display:grid;
134
- place-items:center; font-size:12px; font-weight:700; color:#fff; }
135
- .msg__body { min-width:0; flex:1; }
136
- .msg__head { display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; }
137
- .msg__who { font-weight:600; }
138
- .msg__human { font-size:12px; color:var(--ink3); }
139
- .msg__to { font-size:11.5px; color:var(--terr); }
140
- .msg__time { font-size:11.5px; color:var(--ink3); margin-left:auto; }
141
- .msg__text { margin-top:3px; padding:8px 12px; border-radius:4px 12px 12px 12px;
142
- background:var(--chip); white-space:pre-wrap; word-break:break-word; }
143
- .msg--targeted .msg__text { box-shadow:inset 3px 0 0 var(--terr); }
144
-
145
- /* Operator's own messages — same group-chat structure, mirrored to the right. */
146
- .msg--me { flex-direction:row-reverse; }
147
- .msg--me .msg__head { flex-direction:row-reverse; }
148
- .msg--me .msg__time { margin-left:0; margin-right:auto; }
149
- .msg--me .msg__body { display:flex; flex-direction:column; align-items:flex-end; }
150
- .msg--me .msg__text { border-radius:12px 4px 12px 12px; background:#d4e7d2; }
151
- .msg--me.msg--targeted .msg__text { box-shadow:inset -3px 0 0 var(--terr); }
152
-
153
- /* Composer */
154
- .composer { position:relative; border-top:1px solid var(--line); padding:10px 12px; }
155
- .chat-form { display:flex; gap:8px; }
156
- .chat-input { flex:1; padding:9px 12px; border-radius:8px; border:1px solid var(--line);
157
- background:var(--bg); color:var(--ink); font:inherit; }
158
- .chat-input:focus { outline:none; border-color:var(--terr); }
159
- .chat-send { padding:9px 16px; border-radius:8px; border:1px solid var(--ink);
160
- background:var(--ink); color:var(--bg); font-weight:600; cursor:pointer; font:inherit; }
161
- .chat-send:disabled { opacity:.5; cursor:default; }
162
-
163
- /* @-mention autocomplete — floats above the input */
164
- .mention-pop { position:absolute; left:12px; right:12px; bottom:56px; z-index:6;
165
- background:var(--card); border:1px solid var(--line); border-radius:10px; padding:5px;
166
- box-shadow:0 8px 24px rgba(50,40,20,.16); max-height:200px; overflow-y:auto; }
167
- .mention-mi { display:flex; align-items:center; gap:9px; width:100%; text-align:left;
168
- border:0; appearance:none; font-family:inherit; color:inherit; background:transparent;
169
- border-radius:7px; padding:6px 8px; font-size:13px; cursor:pointer; }
170
- .mention-mi:hover, .mention-mi.on { background:var(--chip); }
171
- .mention-mi .ma { width:22px; height:22px; border-radius:50%; display:grid; place-items:center;
172
- font-size:9px; font-weight:700; color:#fff; flex:none; }
173
-
174
- /* Token gate */
175
- .gate { max-width:420px; margin:12vh auto 0; text-align:center; }
176
- .gate h1 { font-size:19px; margin:0 0 6px; }
177
- .gate p { color:var(--ink2); margin:0 0 20px; }
178
- .gate form { display:flex; gap:8px; }
179
- .gate input { flex:1; padding:10px 12px; border-radius:8px; border:1px solid var(--line);
180
- background:var(--card); color:var(--ink); }
181
- .gate button { padding:10px 16px; border-radius:8px; border:1px solid var(--ink);
182
- background:var(--ink); color:var(--bg); font-weight:600; cursor:pointer; }
1
+ :root {
2
+ --bg:#ece0c6; --ink:#322c20; --ink2:#6f6450; --ink3:#9c917a;
3
+ --line:#d7c9ac; --line2:#e0d4b9; --live:#3d8b58; --drop:#bd4f3a;
4
+ --chip:#e3d6ba; --terr:#7a6a48; --card:#f5ecd9;
5
+ }
6
+ * { box-sizing:border-box; }
7
+ html,body { height:100%; }
8
+ body {
9
+ margin:0; background:var(--bg); color:var(--ink);
10
+ font:14px/1.45 'Inter', ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
11
+ -webkit-font-smoothing:antialiased;
12
+ }
13
+ .wrap { max-width:1100px; margin:0 auto; padding:40px 44px; }
14
+
15
+ header { display:flex; align-items:center; gap:12px; padding-bottom:18px; border-bottom:1px solid var(--line); }
16
+ /* Chat view: the card supplies its own border, so the header rule is redundant. */
17
+ #board:has(#panel-chat:not([hidden])) header { border-bottom:0; }
18
+ .brand { font-size:17px; font-weight:600; }
19
+ .vitals { color:var(--ink3); font-size:12.5px; }
20
+ .grow { flex:1; }
21
+ .status { font-size:12px; color:var(--ink3); }
22
+ .status--ok { color:var(--live); }
23
+ .status--warn { color:var(--terr); }
24
+ .status--error { color:var(--drop); }
25
+ .freshness { font-size:12px; color:var(--ink3); }
26
+
27
+ /* Repo selector (hidden when <2 repos; populated by app.js) */
28
+ .repo { position:relative; }
29
+ .repo-trig { display:inline-flex; align-items:center; gap:6px; font-size:13px;
30
+ font-family:inherit; color:var(--ink); appearance:none;
31
+ border:1px solid var(--line); border-radius:8px; padding:4px 10px;
32
+ background:rgba(255,250,238,.4); cursor:pointer; }
33
+ .repo-trig .slash { color:var(--ink3); }
34
+ .repo-menu { position:absolute; top:36px; left:0; min-width:200px; background:var(--card);
35
+ border:1px solid var(--line); border-radius:10px; padding:5px;
36
+ box-shadow:0 8px 24px rgba(50,40,20,.14); z-index:5; }
37
+ .repo-mi { display:flex; justify-content:space-between; gap:14px; padding:7px 9px;
38
+ width:100%; text-align:left; border:0; appearance:none; font-family:inherit; color:inherit;
39
+ background:transparent; border-radius:7px; font-size:13px; cursor:pointer; }
40
+ .repo-mi:hover { background:var(--chip); }
41
+ .repo-mi.on { background:var(--chip); font-weight:600; }
42
+ .repo-mi .ct { font-size:11px; color:var(--ink3); }
43
+ .repo-mi.all { border-top:1px solid var(--line2); margin-top:4px; padding-top:9px; color:var(--ink2); }
44
+
45
+ /* Tabs */
46
+ .tabs { display:flex; gap:4px; margin-left:auto; }
47
+ .tab { font-size:13px; color:var(--ink2); padding:5px 11px; border:0; border-radius:7px;
48
+ background:transparent; cursor:pointer; font:inherit; }
49
+ .tab--active { background:var(--ink); color:var(--bg); }
50
+
51
+ /* Crew strip */
52
+ .crew { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin:26px 0 32px; }
53
+ .person { display:inline-flex; align-items:center; gap:9px; padding:5px 13px 5px 5px;
54
+ border:1px solid var(--line); border-radius:999px; background:var(--card); font-size:13px; }
55
+ .person--idle { opacity:.55; }
56
+ .avatar { width:24px; height:24px; border-radius:50%; display:grid; place-items:center;
57
+ font-size:10px; font-weight:700; color:#fff; flex:none; }
58
+ .person__name { font-weight:600; }
59
+ .person__doing { color:var(--ink3); font-size:12px; }
60
+
61
+ /* Board */
62
+ .board { display:grid; grid-template-columns:1fr 1px 1fr; gap:0; }
63
+ .board__rule { background:var(--line); }
64
+ .col { padding:0 36px; } .col:first-child { padding-left:0; } .col:last-child { padding-right:0; }
65
+ .colhead { display:flex; align-items:center; gap:8px; margin-bottom:6px; }
66
+ .colhead h2 { font-size:12px; font-weight:600; letter-spacing:.03em; margin:0;
67
+ text-transform:uppercase; color:var(--ink2); }
68
+ .colhead .n { font-size:11px; color:var(--ink3); background:var(--chip);
69
+ border-radius:999px; padding:1px 8px; }
70
+
71
+ .task { padding:16px 0; border-bottom:1px solid var(--line2); }
72
+ .task__r1 { display:flex; align-items:center; gap:8px; }
73
+ .task__who { font-size:13.5px; font-weight:600; }
74
+ .task__tag { font-size:11px; color:var(--ink3); }
75
+ .task__repo { font-size:11px; color:var(--terr); background:rgba(122,106,72,.1);
76
+ border-radius:4px; padding:0 6px; }
77
+ .task__stat { margin-left:auto; font-size:11px; font-weight:500; color:var(--ink3); }
78
+ .task__stat--drop { color:var(--drop); }
79
+ .livedot { width:7px; height:7px; border-radius:50%; background:var(--live); margin-left:auto; }
80
+ .task__intent { font-size:13.5px; margin:5px 0 0; line-height:1.45; }
81
+ .terr { margin-top:8px; display:flex; flex-wrap:wrap; gap:6px; align-items:center; }
82
+ .terr__lbl { font-size:9.5px; text-transform:uppercase; letter-spacing:.05em; color:var(--ink3); }
83
+ .glob { font-family:ui-monospace, Menlo, monospace; font-size:11px; color:var(--terr);
84
+ background:rgba(122,106,72,.12); border:1px solid rgba(122,106,72,.22);
85
+ border-radius:5px; padding:1px 7px; }
86
+ .task__meta { font-size:11.5px; color:var(--ink3); margin-top:6px; }
87
+ /* expandable territory (2+ paths collapse to a one-line "N paths" toggle) */
88
+ .terrx { margin-top:8px; }
89
+ .terrx summary { list-style:none; cursor:pointer; display:flex; align-items:center;
90
+ gap:6px; flex-wrap:wrap; user-select:none; }
91
+ .terrx summary::-webkit-details-marker { display:none; }
92
+ .terrx .terr--full { margin-top:8px; }
93
+ .glob--more { cursor:pointer; color:var(--ink2); background:var(--chip);
94
+ border:1px solid var(--line); }
95
+ .glob--more .tw { color:var(--ink3); display:inline-block; transition:transform .15s; }
96
+ .terrx[open] summary .glob--more .tw { transform:rotate(90deg); }
97
+ /* grouped agent box: header + claims rail + collapsible "narrower" fold */
98
+ .grp { padding:16px 0; border-bottom:1px solid var(--line2); }
99
+ .grp__head { display:flex; align-items:center; gap:8px; }
100
+ .grp__who { font-size:13.5px; font-weight:600; }
101
+ .grp__tag { font-size:11px; color:var(--ink3); }
102
+ .grp__count { font-size:11px; color:var(--ink3); }
103
+ .grp__dot { width:7px; height:7px; border-radius:50%; background:var(--live); margin-left:auto; }
104
+ .claims { margin:10px 0 0 6px; padding-left:14px; border-left:1px solid var(--line); }
105
+ .claim { padding:6px 0; }
106
+ .claim + .claim { border-top:1px solid var(--line2); margin-top:6px; }
107
+ .fold { margin-top:10px; }
108
+ .fold summary { list-style:none; cursor:pointer; font-size:12px; color:var(--ink2);
109
+ display:inline-flex; align-items:center; gap:6px; user-select:none; }
110
+ .fold summary::-webkit-details-marker { display:none; }
111
+ .fold summary .tw { display:inline-block; color:var(--ink3); transition:transform .15s; }
112
+ .fold[open] summary .tw { transform:rotate(90deg); }
113
+ .fold__body { margin:8px 0 0 6px; padding-left:14px; border-left:1px dashed var(--line); }
114
+ .covered { font-size:10.5px; color:var(--ink3); font-style:italic; margin-top:4px; }
115
+ .day { font-size:11px; color:var(--ink3); padding:18px 0 2px; font-weight:600; }
116
+ .more { margin-top:20px; font-size:12.5px; color:var(--ink2); padding:7px 13px;
117
+ border:1px solid var(--line); border-radius:8px; background:rgba(255,250,238,.35);
118
+ cursor:pointer; }
119
+ .empty { color:var(--ink3); font-style:italic; padding:14px 0; }
120
+
121
+ /* Chat (re-themed; structure unchanged) */
122
+ .chat-wrap { background:var(--card); border:1px solid var(--line); border-radius:12px;
123
+ display:flex; flex-direction:column; overflow:hidden; height:calc(100vh - 150px); min-height:320px; }
124
+ .chat { flex:1; overflow-y:auto; padding:16px; }
125
+ .chat { scrollbar-width:thin; scrollbar-color:var(--line) transparent; }
126
+ .chat::-webkit-scrollbar { width:10px; }
127
+ .chat::-webkit-scrollbar-track { background:transparent; }
128
+ .chat::-webkit-scrollbar-thumb { background:var(--line); border-radius:999px;
129
+ border:3px solid var(--card); background-clip:padding-box; }
130
+ .chat::-webkit-scrollbar-thumb:hover { background:var(--ink3); background-clip:padding-box; }
131
+ .chat__note { padding:9px 16px; font-size:11.5px; color:var(--ink3); }
132
+ .msg { display:flex; gap:11px; padding:9px 0; }
133
+ .msg__avatar { flex:none; width:32px; height:32px; border-radius:50%; display:grid;
134
+ place-items:center; font-size:12px; font-weight:700; color:#fff; }
135
+ .msg__body { min-width:0; flex:1; }
136
+ .msg__head { display:flex; align-items:baseline; gap:8px; flex-wrap:wrap; }
137
+ .msg__who { font-weight:600; }
138
+ .msg__human { font-size:12px; color:var(--ink3); }
139
+ .msg__to { font-size:11.5px; color:var(--terr); }
140
+ .msg__time { font-size:11.5px; color:var(--ink3); margin-left:auto; }
141
+ .msg__text { margin-top:3px; padding:8px 12px; border-radius:4px 12px 12px 12px;
142
+ background:var(--chip); white-space:pre-wrap; word-break:break-word; }
143
+ .msg--targeted .msg__text { box-shadow:inset 3px 0 0 var(--terr); }
144
+
145
+ /* Operator's own messages — same group-chat structure, mirrored to the right. */
146
+ .msg--me { flex-direction:row-reverse; }
147
+ .msg--me .msg__head { flex-direction:row-reverse; }
148
+ .msg--me .msg__time { margin-left:0; margin-right:auto; }
149
+ .msg--me .msg__body { display:flex; flex-direction:column; align-items:flex-end; }
150
+ .msg--me .msg__text { border-radius:12px 4px 12px 12px; background:#d4e7d2; }
151
+ .msg--me.msg--targeted .msg__text { box-shadow:inset -3px 0 0 var(--terr); }
152
+
153
+ /* Composer */
154
+ .composer { position:relative; border-top:1px solid var(--line); padding:10px 12px; }
155
+ .chat-form { display:flex; gap:8px; }
156
+ .chat-input { flex:1; padding:9px 12px; border-radius:8px; border:1px solid var(--line);
157
+ background:var(--bg); color:var(--ink); font:inherit; }
158
+ .chat-input:focus { outline:none; border-color:var(--terr); }
159
+ .chat-send { padding:9px 16px; border-radius:8px; border:1px solid var(--ink);
160
+ background:var(--ink); color:var(--bg); font-weight:600; cursor:pointer; font:inherit; }
161
+ .chat-send:disabled { opacity:.5; cursor:default; }
162
+
163
+ /* @-mention autocomplete — floats above the input */
164
+ .mention-pop { position:absolute; left:12px; right:12px; bottom:56px; z-index:6;
165
+ background:var(--card); border:1px solid var(--line); border-radius:10px; padding:5px;
166
+ box-shadow:0 8px 24px rgba(50,40,20,.16); max-height:200px; overflow-y:auto; }
167
+ .mention-mi { display:flex; align-items:center; gap:9px; width:100%; text-align:left;
168
+ border:0; appearance:none; font-family:inherit; color:inherit; background:transparent;
169
+ border-radius:7px; padding:6px 8px; font-size:13px; cursor:pointer; }
170
+ .mention-mi:hover, .mention-mi.on { background:var(--chip); }
171
+ .mention-mi .ma { width:22px; height:22px; border-radius:50%; display:grid; place-items:center;
172
+ font-size:9px; font-weight:700; color:#fff; flex:none; }
173
+
174
+ /* Token gate */
175
+ .gate { max-width:420px; margin:12vh auto 0; text-align:center; }
176
+ .gate h1 { font-size:19px; margin:0 0 6px; }
177
+ .gate p { color:var(--ink2); margin:0 0 20px; }
178
+ .gate form { display:flex; gap:8px; }
179
+ .gate input { flex:1; padding:10px 12px; border-radius:8px; border:1px solid var(--line);
180
+ background:var(--card); color:var(--ink); }
181
+ .gate button { padding:10px 16px; border-radius:8px; border:1px solid var(--ink);
182
+ background:var(--ink); color:var(--bg); font-weight:600; cursor:pointer; }
@@ -1,13 +1,13 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Shepherd</title>
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Shepherd</title>
7
7
  <script type="module" crossorigin src="/assets/index-BBx3vo2Y.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/assets/index-tSyzirZa.css">
9
- </head>
10
- <body>
11
- <div id="root"></div>
12
- </body>
13
- </html>
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+ </body>
13
+ </html>
package/package.json CHANGED
@@ -1,67 +1,67 @@
1
- {
2
- "name": "@korso/shepherd-ui",
3
- "version": "0.1.0",
4
- "description": "Auth-agnostic React UI library for the Shepherd wallboard (Dashboard, client, provider). Host injects auth; the library never reads tokens.",
5
- "type": "module",
6
- "license": "UNLICENSED",
7
- "publishConfig": {
8
- "access": "public"
9
- },
10
- "repository": {
11
- "type": "git",
12
- "url": "git+https://github.com/Korsoai/shepherd.git",
13
- "directory": "packages/ui"
14
- },
15
- "exports": {
16
- ".": {
17
- "import": "./dist/lib/index.js",
18
- "types": "./dist/lib/index.d.ts"
19
- },
20
- "./selfhost": {
21
- "import": "./dist/lib/selfhost.js",
22
- "types": "./dist/lib/selfhost.d.ts"
23
- },
24
- "./styles.css": "./dist/lib/styles.css",
25
- "./package.json": "./package.json"
26
- },
27
- "types": "./dist/lib/index.d.ts",
28
- "files": [
29
- "dist",
30
- "README.md"
31
- ],
32
- "scripts": {
33
- "build": "npm run build:lib && npm run build:app",
34
- "build:lib": "vite build --config vite.config.lib.ts && npm run copy:styles",
35
- "build:app": "vite build --config vite.config.app.ts",
36
- "copy:styles": "node -e \"const fs=require('node:fs'); if (fs.existsSync('src/styles.css')) { fs.mkdirSync('dist/lib', { recursive:true }); fs.copyFileSync('src/styles.css','dist/lib/styles.css'); }\"",
37
- "dev": "vite --config vite.config.app.ts",
38
- "preview": "vite preview --config vite.config.app.ts",
39
- "type-check": "tsc --noEmit",
40
- "test": "vitest run",
41
- "prepublishOnly": "npm run build:lib"
42
- },
43
- "peerDependencies": {
44
- "react": "^18 || ^19",
45
- "react-dom": "^18 || ^19"
46
- },
47
- "dependencies": {
48
- "zod": "^3"
49
- },
50
- "devDependencies": {
51
- "@microsoft/api-extractor": "^7",
52
- "@shepherd/shared": "*",
53
- "@testing-library/jest-dom": "latest",
54
- "@testing-library/react": "latest",
55
- "@testing-library/user-event": "latest",
56
- "@types/react": "^18",
57
- "@types/react-dom": "^18",
58
- "@vitejs/plugin-react": "^4",
59
- "jsdom": "latest",
60
- "react": "^18",
61
- "react-dom": "^18",
62
- "typescript": "^5",
63
- "vite": "^6",
64
- "vite-plugin-dts": "^5",
65
- "vitest": "^4"
66
- }
67
- }
1
+ {
2
+ "name": "@korso/shepherd-ui",
3
+ "version": "0.1.1",
4
+ "description": "Auth-agnostic React UI library for the Shepherd wallboard (Dashboard, client, provider). Host injects auth; the library never reads tokens.",
5
+ "type": "module",
6
+ "license": "UNLICENSED",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/Korsoai/shepherd.git",
13
+ "directory": "packages/ui"
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "import": "./dist/lib/index.js",
18
+ "types": "./dist/lib/index.d.ts"
19
+ },
20
+ "./selfhost": {
21
+ "import": "./dist/lib/selfhost.js",
22
+ "types": "./dist/lib/selfhost.d.ts"
23
+ },
24
+ "./styles.css": "./dist/lib/styles.css",
25
+ "./package.json": "./package.json"
26
+ },
27
+ "types": "./dist/lib/index.d.ts",
28
+ "files": [
29
+ "dist",
30
+ "README.md"
31
+ ],
32
+ "scripts": {
33
+ "build": "npm run build:lib && npm run build:app",
34
+ "build:lib": "vite build --config vite.config.lib.ts && npm run copy:styles",
35
+ "build:app": "vite build --config vite.config.app.ts",
36
+ "copy:styles": "node -e \"const fs=require('node:fs'); if (fs.existsSync('src/styles.css')) { fs.mkdirSync('dist/lib', { recursive:true }); fs.copyFileSync('src/styles.css','dist/lib/styles.css'); }\"",
37
+ "dev": "vite --config vite.config.app.ts",
38
+ "preview": "vite preview --config vite.config.app.ts",
39
+ "type-check": "tsc --noEmit",
40
+ "test": "vitest run",
41
+ "prepublishOnly": "npm run build:lib"
42
+ },
43
+ "peerDependencies": {
44
+ "react": "^18 || ^19",
45
+ "react-dom": "^18 || ^19"
46
+ },
47
+ "dependencies": {
48
+ "zod": "^3"
49
+ },
50
+ "devDependencies": {
51
+ "@microsoft/api-extractor": "^7",
52
+ "@shepherd/shared": "*",
53
+ "@testing-library/jest-dom": "latest",
54
+ "@testing-library/react": "latest",
55
+ "@testing-library/user-event": "latest",
56
+ "@types/react": "^18",
57
+ "@types/react-dom": "^18",
58
+ "@vitejs/plugin-react": "^4",
59
+ "jsdom": "latest",
60
+ "react": "^18",
61
+ "react-dom": "^18",
62
+ "typescript": "^5",
63
+ "vite": "^6",
64
+ "vite-plugin-dts": "^5",
65
+ "vitest": "^4"
66
+ }
67
+ }