@polderlabs/bizar 4.7.0 → 4.7.2
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/bizar-dash/dist/assets/main-DHZmbnxQ.js +361 -0
- package/bizar-dash/dist/assets/main-DHZmbnxQ.js.map +1 -0
- package/bizar-dash/dist/assets/main-DX_Jh8Wc.css +1 -0
- package/bizar-dash/dist/assets/{mobile-CWqPoGaT.js → mobile-BK8-ythT.js} +2 -2
- package/bizar-dash/dist/assets/mobile-BK8-ythT.js.map +1 -0
- package/bizar-dash/dist/assets/{mobile-i4Uv9eW8.js → mobile-Chvf9u_B.js} +1 -1
- package/bizar-dash/dist/assets/{mobile-i4Uv9eW8.js.map → mobile-Chvf9u_B.js.map} +1 -1
- package/bizar-dash/dist/index.html +3 -3
- package/bizar-dash/dist/mobile.html +2 -2
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/bizar-dash/skills/publishing/SKILL.md +146 -0
- package/bizar-dash/src/server/api.mjs +8 -0
- package/bizar-dash/src/server/backup-store.mjs +525 -0
- package/bizar-dash/src/server/digest-store.mjs +558 -0
- package/bizar-dash/src/server/lib/rate-limit.mjs +122 -0
- package/bizar-dash/src/server/routes/backup.mjs +112 -0
- package/bizar-dash/src/server/routes/chat.mjs +14 -0
- package/bizar-dash/src/server/routes/digests.mjs +82 -0
- package/bizar-dash/src/server/routes-v2/events.mjs +14 -0
- package/bizar-dash/src/server/schedules-runner.mjs +126 -0
- package/bizar-dash/src/server/server.mjs +17 -0
- package/bizar-dash/src/web/App.tsx +8 -1
- package/bizar-dash/src/web/components/BackupRestore.tsx +330 -0
- package/bizar-dash/src/web/components/SearchModal.tsx +3 -1
- package/bizar-dash/src/web/components/chat/Composer.tsx +2 -0
- package/bizar-dash/src/web/styles/main.css +70 -8
- package/bizar-dash/src/web/views/Activity.tsx +11 -1
- package/bizar-dash/src/web/views/Agents.tsx +57 -42
- package/bizar-dash/src/web/views/Artifacts.tsx +38 -25
- package/bizar-dash/src/web/views/Chat.tsx +8 -0
- package/bizar-dash/src/web/views/History.tsx +4 -1
- package/bizar-dash/src/web/views/MiniMaxUsage.tsx +21 -4
- package/bizar-dash/src/web/views/Mods.tsx +30 -17
- package/bizar-dash/src/web/views/Overview.tsx +8 -1
- package/bizar-dash/src/web/views/Providers.tsx +16 -16
- package/bizar-dash/src/web/views/Schedules.tsx +33 -15
- package/bizar-dash/src/web/views/Settings.tsx +97 -1751
- package/bizar-dash/src/web/views/Skills.tsx +4 -1
- package/bizar-dash/src/web/views/Tasks.tsx +11 -2
- package/bizar-dash/src/web/views/memory/ConfigPanel.tsx +8 -2
- package/bizar-dash/src/web/views/memory/LightragPanel.tsx +3 -0
- package/bizar-dash/src/web/views/memory/ObsidianPanel.tsx +12 -4
- package/bizar-dash/src/web/views/memory/SemanticSearchPanel.tsx +3 -0
- package/bizar-dash/src/web/views/settings/ActivitySection.tsx +205 -0
- package/bizar-dash/src/web/views/settings/AgentSection.tsx +294 -0
- package/bizar-dash/src/web/views/settings/AuthSection.tsx +159 -0
- package/bizar-dash/src/web/views/settings/BackupSection.tsx +16 -0
- package/bizar-dash/src/web/views/settings/EnvVarsSection.tsx +16 -0
- package/bizar-dash/src/web/views/settings/GeneralSection.tsx +105 -0
- package/bizar-dash/src/web/views/settings/HeadroomSection.tsx +39 -0
- package/bizar-dash/src/web/views/settings/MemorySection.tsx +16 -0
- package/bizar-dash/src/web/views/settings/NetworkSection.tsx +87 -0
- package/bizar-dash/src/web/views/settings/NotificationsSection.tsx +34 -0
- package/bizar-dash/src/web/views/settings/ProvidersSection.tsx +16 -0
- package/bizar-dash/src/web/views/settings/SkillsSection.tsx +16 -0
- package/bizar-dash/src/web/views/settings/SystemLlmSection.tsx +81 -0
- package/bizar-dash/src/web/views/settings/ThemeSection.tsx +168 -0
- package/bizar-dash/src/web/views/settings/UpdatesSection.tsx +256 -0
- package/bizar-dash/tests/a11y.test.tsx +206 -0
- package/bizar-dash/tests/backup-restore.test.mjs +217 -0
- package/bizar-dash/tests/backup-restore.test.tsx +123 -0
- package/bizar-dash/tests/backup-store.test.mjs +300 -0
- package/bizar-dash/tests/cli-error-visibility.test.mjs +153 -0
- package/bizar-dash/tests/digest-generation.test.mjs +191 -0
- package/bizar-dash/tests/digest-store.test.mjs +264 -0
- package/bizar-dash/tests/rate-limit.test.mjs +298 -0
- package/cli/bin.mjs +96 -2
- package/cli/commands/minimax.mjs +20 -0
- package/cli/commands/util.mjs +154 -1
- package/cli/digest.mjs +149 -0
- package/package.json +1 -1
- package/bizar-dash/dist/assets/main-DAlLdW8I.css +0 -1
- package/bizar-dash/dist/assets/main-DGGq-iZI.js +0 -361
- package/bizar-dash/dist/assets/main-DGGq-iZI.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-CWqPoGaT.js.map +0 -1
|
@@ -144,16 +144,18 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
|
|
|
144
144
|
width: 640,
|
|
145
145
|
children: (
|
|
146
146
|
<div className="agent-form">
|
|
147
|
-
<label className="field-label">Name (a-z, 0-9, dashes)</label>
|
|
147
|
+
<label className="field-label" htmlFor="agent-new-name">Name (a-z, 0-9, dashes)</label>
|
|
148
148
|
<input
|
|
149
|
+
id="agent-new-name"
|
|
149
150
|
ref={(el) => (nameEl = el)}
|
|
150
151
|
className="input"
|
|
151
152
|
type="text"
|
|
152
153
|
placeholder="my-agent"
|
|
153
154
|
autoFocus
|
|
154
155
|
/>
|
|
155
|
-
<label className="field-label">Description</label>
|
|
156
|
+
<label className="field-label" htmlFor="agent-new-desc">Description</label>
|
|
156
157
|
<input
|
|
158
|
+
id="agent-new-desc"
|
|
157
159
|
ref={(el) => { descEl = el; }}
|
|
158
160
|
className="input"
|
|
159
161
|
type="text"
|
|
@@ -161,8 +163,8 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
|
|
|
161
163
|
/>
|
|
162
164
|
<div className="task-form-row">
|
|
163
165
|
<div className="task-form-field">
|
|
164
|
-
<label className="field-label">Model</label>
|
|
165
|
-
<select ref={(el) => (modelEl = el)} className="select" defaultValue="">
|
|
166
|
+
<label className="field-label" htmlFor="agent-new-model">Model</label>
|
|
167
|
+
<select id="agent-new-model" ref={(el) => (modelEl = el)} className="select" defaultValue="">
|
|
166
168
|
<option value="">(provider default)</option>
|
|
167
169
|
{MODELS.map((m) => (
|
|
168
170
|
<option key={m} value={m}>{m}</option>
|
|
@@ -170,22 +172,22 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
|
|
|
170
172
|
</select>
|
|
171
173
|
</div>
|
|
172
174
|
<div className="task-form-field">
|
|
173
|
-
<label className="field-label">Mode</label>
|
|
174
|
-
<select ref={(el) => (modeEl = el)} className="select" defaultValue="subagent">
|
|
175
|
+
<label className="field-label" htmlFor="agent-new-mode">Mode</label>
|
|
176
|
+
<select id="agent-new-mode" ref={(el) => (modeEl = el)} className="select" defaultValue="subagent">
|
|
175
177
|
<option value="primary">primary</option>
|
|
176
178
|
<option value="subagent">subagent</option>
|
|
177
179
|
<option value="all">all</option>
|
|
178
180
|
</select>
|
|
179
181
|
</div>
|
|
180
182
|
<div className="task-form-field" style={{ flex: '0 0 80px' }}>
|
|
181
|
-
<label className="field-label">Color</label>
|
|
182
|
-
<input ref={(el) => (colorEl = el)} className="input" type="color" defaultValue="#8b5cf6" />
|
|
183
|
+
<label className="field-label" htmlFor="agent-new-color">Color</label>
|
|
184
|
+
<input id="agent-new-color" ref={(el) => (colorEl = el)} className="input" type="color" defaultValue="#8b5cf6" />
|
|
183
185
|
</div>
|
|
184
186
|
</div>
|
|
185
187
|
<div className="task-form-row">
|
|
186
188
|
<div className="task-form-field" style={{ flex: 1 }}>
|
|
187
|
-
<label className="field-label">Category</label>
|
|
188
|
-
<select ref={(el) => (categoryEl = el)} className="select" defaultValue="">
|
|
189
|
+
<label className="field-label" htmlFor="agent-new-category">Category</label>
|
|
190
|
+
<select id="agent-new-category" ref={(el) => (categoryEl = el)} className="select" defaultValue="">
|
|
189
191
|
<option value="">(none)</option>
|
|
190
192
|
{CATEGORIES.map((c) => (
|
|
191
193
|
<option key={c.id} value={c.id}>{c.label}</option>
|
|
@@ -193,21 +195,24 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
|
|
|
193
195
|
</select>
|
|
194
196
|
</div>
|
|
195
197
|
<div className="task-form-field" style={{ flex: 2 }}>
|
|
196
|
-
<label className="field-label">Tags (comma-separated)</label>
|
|
197
|
-
<input ref={(el) => (tagsEl = el)} className="input" type="text" placeholder="reasoning, code, planning" />
|
|
198
|
+
<label className="field-label" htmlFor="agent-new-tags">Tags (comma-separated)</label>
|
|
199
|
+
<input id="agent-new-tags" ref={(el) => (tagsEl = el)} className="input" type="text" placeholder="reasoning, code, planning" />
|
|
198
200
|
</div>
|
|
199
201
|
</div>
|
|
200
|
-
<
|
|
201
|
-
|
|
202
|
-
{
|
|
203
|
-
|
|
204
|
-
<
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
202
|
+
<fieldset>
|
|
203
|
+
<legend className="field-label" style={{ padding: 0 }}>Tools</legend>
|
|
204
|
+
<div ref={(el) => (toolsContainer = el)} className="agent-tools">
|
|
205
|
+
{TOOL_OPTIONS.map((t) => (
|
|
206
|
+
<label key={t} className="checkbox-row">
|
|
207
|
+
<input type="checkbox" value={t} aria-label={t} />
|
|
208
|
+
<span>{t}</span>
|
|
209
|
+
</label>
|
|
210
|
+
))}
|
|
211
|
+
</div>
|
|
212
|
+
</fieldset>
|
|
213
|
+
<label className="field-label" htmlFor="agent-new-prompt">System prompt</label>
|
|
210
214
|
<textarea
|
|
215
|
+
id="agent-new-prompt"
|
|
211
216
|
ref={(el) => (promptEl = el)}
|
|
212
217
|
className="textarea"
|
|
213
218
|
rows={6}
|
|
@@ -280,8 +285,9 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
|
|
|
280
285
|
<div className="muted">
|
|
281
286
|
File: <code>{full.path}</code>
|
|
282
287
|
</div>
|
|
283
|
-
<label className="field-label">Description</label>
|
|
288
|
+
<label className="field-label" htmlFor="agent-edit-desc">Description</label>
|
|
284
289
|
<input
|
|
290
|
+
id="agent-edit-desc"
|
|
285
291
|
ref={(el) => { descEl = el; }}
|
|
286
292
|
className="input"
|
|
287
293
|
type="text"
|
|
@@ -289,8 +295,8 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
|
|
|
289
295
|
/>
|
|
290
296
|
<div className="task-form-row">
|
|
291
297
|
<div className="task-form-field">
|
|
292
|
-
<label className="field-label">Model</label>
|
|
293
|
-
<select ref={(el) => (modelEl = el)} className="select" defaultValue={full.model}>
|
|
298
|
+
<label className="field-label" htmlFor="agent-edit-model">Model</label>
|
|
299
|
+
<select id="agent-edit-model" ref={(el) => (modelEl = el)} className="select" defaultValue={full.model}>
|
|
294
300
|
<option value="">(provider default)</option>
|
|
295
301
|
{MODELS.map((m) => (
|
|
296
302
|
<option key={m} value={m}>{m}</option>
|
|
@@ -298,22 +304,22 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
|
|
|
298
304
|
</select>
|
|
299
305
|
</div>
|
|
300
306
|
<div className="task-form-field">
|
|
301
|
-
<label className="field-label">Mode</label>
|
|
302
|
-
<select ref={(el) => (modeEl = el)} className="select" defaultValue={full.mode || 'subagent'}>
|
|
307
|
+
<label className="field-label" htmlFor="agent-edit-mode">Mode</label>
|
|
308
|
+
<select id="agent-edit-mode" ref={(el) => (modeEl = el)} className="select" defaultValue={full.mode || 'subagent'}>
|
|
303
309
|
<option value="primary">primary</option>
|
|
304
310
|
<option value="subagent">subagent</option>
|
|
305
311
|
<option value="all">all</option>
|
|
306
312
|
</select>
|
|
307
313
|
</div>
|
|
308
314
|
<div className="task-form-field" style={{ flex: '0 0 80px' }}>
|
|
309
|
-
<label className="field-label">Color</label>
|
|
310
|
-
<input ref={(el) => (colorEl = el)} className="input" type="color" defaultValue={full.color || '#8b5cf6'} />
|
|
315
|
+
<label className="field-label" htmlFor="agent-edit-color">Color</label>
|
|
316
|
+
<input id="agent-edit-color" ref={(el) => (colorEl = el)} className="input" type="color" defaultValue={full.color || '#8b5cf6'} />
|
|
311
317
|
</div>
|
|
312
318
|
</div>
|
|
313
319
|
<div className="task-form-row">
|
|
314
320
|
<div className="task-form-field" style={{ flex: 1 }}>
|
|
315
|
-
<label className="field-label">Category</label>
|
|
316
|
-
<select ref={(el) => (categoryEl = el)} className="select" defaultValue={full.category || ''}>
|
|
321
|
+
<label className="field-label" htmlFor="agent-edit-category">Category</label>
|
|
322
|
+
<select id="agent-edit-category" ref={(el) => (categoryEl = el)} className="select" defaultValue={full.category || ''}>
|
|
317
323
|
<option value="">(none)</option>
|
|
318
324
|
{CATEGORIES.map((c) => (
|
|
319
325
|
<option key={c.id} value={c.id}>{c.label}</option>
|
|
@@ -321,8 +327,9 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
|
|
|
321
327
|
</select>
|
|
322
328
|
</div>
|
|
323
329
|
<div className="task-form-field" style={{ flex: 2 }}>
|
|
324
|
-
<label className="field-label">Tags (comma-separated)</label>
|
|
330
|
+
<label className="field-label" htmlFor="agent-edit-tags">Tags (comma-separated)</label>
|
|
325
331
|
<input
|
|
332
|
+
id="agent-edit-tags"
|
|
326
333
|
ref={(el) => (tagsEl = el)}
|
|
327
334
|
className="input"
|
|
328
335
|
type="text"
|
|
@@ -330,17 +337,20 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
|
|
|
330
337
|
/>
|
|
331
338
|
</div>
|
|
332
339
|
</div>
|
|
333
|
-
<
|
|
334
|
-
|
|
335
|
-
{
|
|
336
|
-
|
|
337
|
-
<
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
340
|
+
<fieldset>
|
|
341
|
+
<legend className="field-label" style={{ padding: 0 }}>Tools</legend>
|
|
342
|
+
<div ref={(el) => (toolsContainer = el)} className="agent-tools">
|
|
343
|
+
{TOOL_OPTIONS.map((t) => (
|
|
344
|
+
<label key={t} className="checkbox-row">
|
|
345
|
+
<input type="checkbox" value={t} aria-label={t} defaultChecked={full.tools?.includes(t)} />
|
|
346
|
+
<span>{t}</span>
|
|
347
|
+
</label>
|
|
348
|
+
))}
|
|
349
|
+
</div>
|
|
350
|
+
</fieldset>
|
|
351
|
+
<label className="field-label" htmlFor="agent-edit-prompt">System prompt</label>
|
|
343
352
|
<textarea
|
|
353
|
+
id="agent-edit-prompt"
|
|
344
354
|
ref={(el) => (promptEl = el)}
|
|
345
355
|
className="textarea"
|
|
346
356
|
rows={8}
|
|
@@ -483,15 +493,20 @@ export function Agents({ snapshot, refreshSnapshot }: Props) {
|
|
|
483
493
|
</div>
|
|
484
494
|
<div className="view-actions">
|
|
485
495
|
<div className="search-input">
|
|
496
|
+
<label htmlFor="agents-search" className="sr-only">Search agents</label>
|
|
486
497
|
<input
|
|
498
|
+
id="agents-search"
|
|
487
499
|
className="input"
|
|
488
500
|
type="text"
|
|
489
501
|
placeholder="Search…"
|
|
490
502
|
value={search}
|
|
491
503
|
onChange={(e) => setSearch(e.target.value)}
|
|
504
|
+
aria-label="Search agents"
|
|
492
505
|
/>
|
|
493
506
|
</div>
|
|
507
|
+
<label htmlFor="agents-category-filter" className="sr-only">Filter by category</label>
|
|
494
508
|
<select
|
|
509
|
+
id="agents-category-filter"
|
|
495
510
|
className="select select-sm"
|
|
496
511
|
value={categoryFilter}
|
|
497
512
|
onChange={(e) => setCategoryFilter(e.target.value)}
|
|
@@ -200,13 +200,16 @@ export function Artifacts({ snapshot, refreshSnapshot }: Props) {
|
|
|
200
200
|
</div>
|
|
201
201
|
<div className="view-actions">
|
|
202
202
|
<div className="search-input">
|
|
203
|
-
<Search size={14} />
|
|
203
|
+
<Search size={14} aria-hidden />
|
|
204
|
+
<label htmlFor="artifacts-search" className="sr-only">Search artifacts</label>
|
|
204
205
|
<input
|
|
206
|
+
id="artifacts-search"
|
|
205
207
|
className="input"
|
|
206
208
|
type="text"
|
|
207
209
|
placeholder="Search…"
|
|
208
210
|
value={filter}
|
|
209
211
|
onChange={(e) => setFilter(e.target.value)}
|
|
212
|
+
aria-label="Search artifacts"
|
|
210
213
|
/>
|
|
211
214
|
</div>
|
|
212
215
|
<Button
|
|
@@ -275,7 +278,9 @@ function NewPlanCard({ onCreate }: { onCreate: (slug: string, title?: string) =>
|
|
|
275
278
|
setTitle('');
|
|
276
279
|
}}
|
|
277
280
|
>
|
|
281
|
+
<label htmlFor="new-artifact-slug" className="sr-only">Artifact slug</label>
|
|
278
282
|
<input
|
|
283
|
+
id="new-artifact-slug"
|
|
279
284
|
className="input"
|
|
280
285
|
type="text"
|
|
281
286
|
placeholder="slug (e.g. dashboard-v3.1)"
|
|
@@ -283,8 +288,11 @@ function NewPlanCard({ onCreate }: { onCreate: (slug: string, title?: string) =>
|
|
|
283
288
|
required
|
|
284
289
|
value={slug}
|
|
285
290
|
onChange={(e) => setSlug(e.target.value)}
|
|
291
|
+
aria-label="Artifact slug"
|
|
286
292
|
/>
|
|
293
|
+
<label htmlFor="new-artifact-title" className="sr-only">Artifact title</label>
|
|
287
294
|
<input
|
|
295
|
+
id="new-artifact-title"
|
|
288
296
|
className="input"
|
|
289
297
|
type="text"
|
|
290
298
|
placeholder="Title (optional)"
|
|
@@ -545,14 +553,14 @@ function PlanEditor({
|
|
|
545
553
|
width: 560,
|
|
546
554
|
children: (
|
|
547
555
|
<div className="artifact-config-form">
|
|
548
|
-
<label className="field-label">Title</label>
|
|
549
|
-
<input ref={(el) => { titleEl = el; }} className="input" type="text" defaultValue={meta.title} />
|
|
550
|
-
<label className="field-label">Description (markdown)</label>
|
|
551
|
-
<textarea ref={(el) => { descEl = el; }} className="textarea" rows={3} defaultValue={meta.description || ''} />
|
|
552
|
-
<label className="field-label">Tags (comma-separated)</label>
|
|
553
|
-
<input ref={(el) => { tagsEl = el; }} className="input" type="text" defaultValue={(meta.tags || []).join(', ')} />
|
|
554
|
-
<label className="field-label">Status</label>
|
|
555
|
-
<select ref={(el) => { statusEl = el; }} className="select" defaultValue={meta.status}>
|
|
556
|
+
<label className="field-label" htmlFor="artifact-cfg-title">Title</label>
|
|
557
|
+
<input id="artifact-cfg-title" ref={(el) => { titleEl = el; }} className="input" type="text" defaultValue={meta.title} />
|
|
558
|
+
<label className="field-label" htmlFor="artifact-cfg-desc">Description (markdown)</label>
|
|
559
|
+
<textarea id="artifact-cfg-desc" ref={(el) => { descEl = el; }} className="textarea" rows={3} defaultValue={meta.description || ''} />
|
|
560
|
+
<label className="field-label" htmlFor="artifact-cfg-tags">Tags (comma-separated)</label>
|
|
561
|
+
<input id="artifact-cfg-tags" ref={(el) => { tagsEl = el; }} className="input" type="text" defaultValue={(meta.tags || []).join(', ')} />
|
|
562
|
+
<label className="field-label" htmlFor="artifact-cfg-status">Status</label>
|
|
563
|
+
<select id="artifact-cfg-status" ref={(el) => { statusEl = el; }} className="select" defaultValue={meta.status}>
|
|
556
564
|
{PLAN_STATUSES.map((s) => (
|
|
557
565
|
<option key={s} value={s}>{s}</option>
|
|
558
566
|
))}
|
|
@@ -597,16 +605,16 @@ function PlanEditor({
|
|
|
597
605
|
width: 520,
|
|
598
606
|
children: (
|
|
599
607
|
<div className="artifact-element-form">
|
|
600
|
-
<label className="field-label">Type</label>
|
|
601
|
-
<select ref={(el) => { typeEl = el; }} className="select" defaultValue="task">
|
|
608
|
+
<label className="field-label" htmlFor="artifact-elem-type">Type</label>
|
|
609
|
+
<select id="artifact-elem-type" ref={(el) => { typeEl = el; }} className="select" defaultValue="task">
|
|
602
610
|
{ELEMENT_TYPES.map((t) => (
|
|
603
611
|
<option key={t.id} value={t.id}>{t.label}</option>
|
|
604
612
|
))}
|
|
605
613
|
</select>
|
|
606
|
-
<label className="field-label">Title</label>
|
|
607
|
-
<input ref={(el) => { titleEl = el; }} className="input" type="text" placeholder="Element title" autoFocus />
|
|
608
|
-
<label className="field-label">Content (markdown)</label>
|
|
609
|
-
<textarea ref={(el) => { contentEl = el; }} className="textarea" rows={4} placeholder="Description…" />
|
|
614
|
+
<label className="field-label" htmlFor="artifact-elem-title">Title</label>
|
|
615
|
+
<input id="artifact-elem-title" ref={(el) => { titleEl = el; }} className="input" type="text" placeholder="Element title" autoFocus />
|
|
616
|
+
<label className="field-label" htmlFor="artifact-elem-content">Content (markdown)</label>
|
|
617
|
+
<textarea id="artifact-elem-content" ref={(el) => { contentEl = el; }} className="textarea" rows={4} placeholder="Description…" />
|
|
610
618
|
</div>
|
|
611
619
|
),
|
|
612
620
|
footer: (
|
|
@@ -653,8 +661,8 @@ function PlanEditor({
|
|
|
653
661
|
width: 480,
|
|
654
662
|
children: (
|
|
655
663
|
<div>
|
|
656
|
-
<label className="field-label">Comment</label>
|
|
657
|
-
<textarea ref={(el) => { textEl = el; }} className="textarea" rows={4} placeholder="Comment for the whole artifact…" autoFocus />
|
|
664
|
+
<label className="field-label" htmlFor="canvas-comment">Comment</label>
|
|
665
|
+
<textarea id="canvas-comment" ref={(el) => { textEl = el; }} className="textarea" rows={4} placeholder="Comment for the whole artifact…" autoFocus />
|
|
658
666
|
</div>
|
|
659
667
|
),
|
|
660
668
|
footer: (
|
|
@@ -908,18 +916,18 @@ function editElementInline(
|
|
|
908
916
|
width: 520,
|
|
909
917
|
children: (
|
|
910
918
|
<div className="artifact-element-form">
|
|
911
|
-
<label className="field-label">Type</label>
|
|
912
|
-
<select ref={(el2) => { typeEl = el2; }} className="select" defaultValue={el.type}>
|
|
919
|
+
<label className="field-label" htmlFor="elem-edit-type">Type</label>
|
|
920
|
+
<select id="elem-edit-type" ref={(el2) => { typeEl = el2; }} className="select" defaultValue={el.type}>
|
|
913
921
|
{ELEMENT_TYPES.map((t) => (
|
|
914
922
|
<option key={t.id} value={t.id}>{t.label}</option>
|
|
915
923
|
))}
|
|
916
924
|
</select>
|
|
917
|
-
<label className="field-label">Title</label>
|
|
918
|
-
<input ref={(el2) => { titleEl = el2; }} className="input" type="text" defaultValue={el.title} />
|
|
919
|
-
<label className="field-label">Status</label>
|
|
920
|
-
<input ref={(el2) => { statusEl = el2; }} className="input" type="text" defaultValue={el.status} placeholder="open / done / blocked …" />
|
|
921
|
-
<label className="field-label">Content (markdown)</label>
|
|
922
|
-
<textarea ref={(el2) => { contentEl = el2; }} className="textarea" rows={5} defaultValue={el.content} />
|
|
925
|
+
<label className="field-label" htmlFor="elem-edit-title">Title</label>
|
|
926
|
+
<input id="elem-edit-title" ref={(el2) => { titleEl = el2; }} className="input" type="text" defaultValue={el.title} />
|
|
927
|
+
<label className="field-label" htmlFor="elem-edit-status">Status</label>
|
|
928
|
+
<input id="elem-edit-status" ref={(el2) => { statusEl = el2; }} className="input" type="text" defaultValue={el.status} placeholder="open / done / blocked …" />
|
|
929
|
+
<label className="field-label" htmlFor="elem-edit-content">Content (markdown)</label>
|
|
930
|
+
<textarea id="elem-edit-content" ref={(el2) => { contentEl = el2; }} className="textarea" rows={5} defaultValue={el.content} />
|
|
923
931
|
</div>
|
|
924
932
|
),
|
|
925
933
|
footer: (
|
|
@@ -1464,12 +1472,17 @@ function CommentsPanel({
|
|
|
1464
1472
|
setText('');
|
|
1465
1473
|
}}
|
|
1466
1474
|
>
|
|
1475
|
+
<label htmlFor="artifacts-comment-input" className="sr-only">
|
|
1476
|
+
{selectedElId ? 'Add a comment to this element' : 'Add a canvas comment'}
|
|
1477
|
+
</label>
|
|
1467
1478
|
<textarea
|
|
1479
|
+
id="artifacts-comment-input"
|
|
1468
1480
|
className="textarea"
|
|
1469
1481
|
rows={2}
|
|
1470
1482
|
placeholder={selectedElId ? 'Add a comment to this element…' : 'Add a canvas comment…'}
|
|
1471
1483
|
value={text}
|
|
1472
1484
|
onChange={(e) => setText(e.target.value)}
|
|
1485
|
+
aria-label={selectedElId ? 'Add a comment to this element' : 'Add a canvas comment'}
|
|
1473
1486
|
/>
|
|
1474
1487
|
<Button variant="primary" size="sm" type="submit" disabled={!text.trim()}>
|
|
1475
1488
|
<Send size={12} /> Post
|
|
@@ -141,8 +141,10 @@ export function Chat({
|
|
|
141
141
|
title: 'Rename session',
|
|
142
142
|
children: (
|
|
143
143
|
<input
|
|
144
|
+
id="rename-session-input"
|
|
144
145
|
autoFocus
|
|
145
146
|
defaultValue={currentTitle}
|
|
147
|
+
aria-label="Session title"
|
|
146
148
|
onChange={(e) => {
|
|
147
149
|
next = e.target.value;
|
|
148
150
|
}}
|
|
@@ -406,6 +408,10 @@ export function Chat({
|
|
|
406
408
|
className="chat-thread-scroll"
|
|
407
409
|
ref={chat.listRef}
|
|
408
410
|
onScroll={chat.handleScroll}
|
|
411
|
+
role="log"
|
|
412
|
+
aria-live="polite"
|
|
413
|
+
aria-relevant="additions"
|
|
414
|
+
aria-label="Chat message thread"
|
|
409
415
|
>
|
|
410
416
|
<ChatThread
|
|
411
417
|
messages={
|
|
@@ -463,6 +469,8 @@ export function Chat({
|
|
|
463
469
|
multiple
|
|
464
470
|
style={{ display: 'none' }}
|
|
465
471
|
onChange={onFiles}
|
|
472
|
+
aria-label="Attach files to message"
|
|
473
|
+
tabIndex={-1}
|
|
466
474
|
/>
|
|
467
475
|
</section>
|
|
468
476
|
|
|
@@ -234,12 +234,15 @@ export function History({ snapshot }: Props) {
|
|
|
234
234
|
</div>
|
|
235
235
|
<div className="view-actions">
|
|
236
236
|
<div className="tasks-toolbar-group">
|
|
237
|
-
<Filter size={14} style={{ color: 'var(--text-dim)' }} />
|
|
237
|
+
<Filter size={14} style={{ color: 'var(--text-dim)' }} aria-hidden />
|
|
238
|
+
<label htmlFor="history-range" className="sr-only">Time range</label>
|
|
238
239
|
<select
|
|
240
|
+
id="history-range"
|
|
239
241
|
className="select select-sm"
|
|
240
242
|
value={rangeId}
|
|
241
243
|
onChange={(e) => setRangeId(e.target.value)}
|
|
242
244
|
title="Time range"
|
|
245
|
+
aria-label="Time range"
|
|
243
246
|
>
|
|
244
247
|
{TIME_RANGES.map((r) => (
|
|
245
248
|
<option key={r.id} value={r.id}>{r.label}</option>
|
|
@@ -585,18 +585,24 @@ function AnalyticsView({ toast }: { toast: ReturnType<typeof useToast> }) {
|
|
|
585
585
|
))}
|
|
586
586
|
{range === 'custom' && (
|
|
587
587
|
<div className="usage-custom-dates">
|
|
588
|
+
<label htmlFor="usage-from-date" className="sr-only">From date</label>
|
|
588
589
|
<input
|
|
590
|
+
id="usage-from-date"
|
|
589
591
|
type="date"
|
|
590
592
|
className="usage-date-input"
|
|
591
593
|
value={customFrom}
|
|
592
594
|
onChange={e => setCustomFrom(e.target.value)}
|
|
595
|
+
aria-label="From date"
|
|
593
596
|
/>
|
|
594
|
-
<span style={{ color: 'var(--text-muted)', fontSize: 12 }}>to</span>
|
|
597
|
+
<span style={{ color: 'var(--text-muted)', fontSize: 12 }} aria-hidden>to</span>
|
|
598
|
+
<label htmlFor="usage-to-date" className="sr-only">To date</label>
|
|
595
599
|
<input
|
|
600
|
+
id="usage-to-date"
|
|
596
601
|
type="date"
|
|
597
602
|
className="usage-date-input"
|
|
598
603
|
value={customTo}
|
|
599
604
|
onChange={e => setCustomTo(e.target.value)}
|
|
605
|
+
aria-label="To date"
|
|
600
606
|
/>
|
|
601
607
|
</div>
|
|
602
608
|
)}
|
|
@@ -879,9 +885,20 @@ function OnboardingWizard({ step, setStep, keyDraft, setKeyDraft, showKey, setSh
|
|
|
879
885
|
<p className="minimax-wizard-prose">Paste your key below. We'll test it against the real API before saving.</p>
|
|
880
886
|
<div className="minimax-key-row">
|
|
881
887
|
<div className="minimax-key-input-wrap">
|
|
882
|
-
<KeyRound size={12} className="minimax-key-icon" />
|
|
883
|
-
<
|
|
884
|
-
|
|
888
|
+
<KeyRound size={12} className="minimax-key-icon" aria-hidden />
|
|
889
|
+
<label htmlFor="minimax-key-input" className="sr-only">Subscription Key</label>
|
|
890
|
+
<input
|
|
891
|
+
id="minimax-key-input"
|
|
892
|
+
type={showKey ? 'text' : 'password'}
|
|
893
|
+
value={keyDraft}
|
|
894
|
+
onChange={e => setKeyDraft(e.target.value)}
|
|
895
|
+
placeholder="sk-cp-..."
|
|
896
|
+
spellCheck={false}
|
|
897
|
+
autoComplete="off"
|
|
898
|
+
className="minimax-key-input"
|
|
899
|
+
autoFocus
|
|
900
|
+
aria-label="Subscription Key"
|
|
901
|
+
/>
|
|
885
902
|
<button type="button" onClick={() => setShowKey(v => !v)} className="minimax-key-toggle" aria-label={showKey ? 'Hide key' : 'Show key'}>
|
|
886
903
|
{showKey ? <EyeOff size={13} /> : <Eye size={13} />}
|
|
887
904
|
</button>
|
|
@@ -204,8 +204,9 @@ export function Mods({ snapshot, refreshSnapshot }: Props) {
|
|
|
204
204
|
a <code>mod.json</code>). The folder will be copied to
|
|
205
205
|
{' '}<code>~/.config/bizar/mods/<id>/</code>.
|
|
206
206
|
</p>
|
|
207
|
-
<label className="field-label">Path</label>
|
|
207
|
+
<label className="field-label" htmlFor="install-mod-path">Path</label>
|
|
208
208
|
<input
|
|
209
|
+
id="install-mod-path"
|
|
209
210
|
ref={(el) => (pathEl = el)}
|
|
210
211
|
className="input"
|
|
211
212
|
type="text"
|
|
@@ -355,20 +356,26 @@ export function Mods({ snapshot, refreshSnapshot }: Props) {
|
|
|
355
356
|
|
|
356
357
|
{/* v3.16.0 — Registry browser */}
|
|
357
358
|
<Card className="mods-registry-card">
|
|
358
|
-
<
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
359
|
+
<button
|
|
360
|
+
type="button"
|
|
361
|
+
className="mods-registry-head"
|
|
362
|
+
aria-expanded={registryOpen}
|
|
363
|
+
aria-controls="mods-registry-body"
|
|
364
|
+
onClick={() => {
|
|
365
|
+
if (!registry && !registryLoading) loadRegistry();
|
|
366
|
+
setRegistryOpen((v) => !v);
|
|
367
|
+
}}
|
|
368
|
+
>
|
|
369
|
+
<Globe size={14} aria-hidden />
|
|
363
370
|
<span className="mods-registry-title">Mod registry</span>
|
|
364
371
|
<span className="muted" style={{ fontSize: 11 }}>
|
|
365
372
|
{registry?.mods ? `${registry.mods.length} available` : 'click to browse'}
|
|
366
373
|
</span>
|
|
367
374
|
<span className="mods-registry-spacer" />
|
|
368
|
-
{registryOpen ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
|
|
369
|
-
</
|
|
375
|
+
{registryOpen ? <ChevronDown size={14} aria-hidden /> : <ChevronRight size={14} aria-hidden />}
|
|
376
|
+
</button>
|
|
370
377
|
{registryOpen && (
|
|
371
|
-
<div className="mods-registry-body">
|
|
378
|
+
<div id="mods-registry-body" className="mods-registry-body">
|
|
372
379
|
{registryLoading ? (
|
|
373
380
|
<div className="muted" style={{ padding: 12, fontSize: 12 }}>Loading registry…</div>
|
|
374
381
|
) : registry?.error ? (
|
|
@@ -586,20 +593,26 @@ function ModDetails({ mod }: { mod: Mod }) {
|
|
|
586
593
|
|
|
587
594
|
{/* v3.20 — Installed instructions (agents / commands / skills) */}
|
|
588
595
|
<div className="mod-instructions">
|
|
589
|
-
<
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
596
|
+
<button
|
|
597
|
+
type="button"
|
|
598
|
+
className="mod-instructions-head"
|
|
599
|
+
aria-expanded={instructionsOpen}
|
|
600
|
+
aria-controls={`mod-instructions-body-${mod.id}`}
|
|
601
|
+
onClick={() => {
|
|
602
|
+
if (!instructions && !instructionsLoading) loadInstructions();
|
|
603
|
+
setInstructionsOpen((v) => !v);
|
|
604
|
+
}}
|
|
605
|
+
>
|
|
606
|
+
<FileCode size={14} aria-hidden />
|
|
594
607
|
<span>Installed instructions</span>
|
|
595
608
|
<span className="muted" style={{ fontSize: 11 }}>
|
|
596
609
|
{instructions ? `${instructions.total} files` : 'click to view'}
|
|
597
610
|
</span>
|
|
598
611
|
<span className="mod-instructions-spacer" />
|
|
599
|
-
{instructionsOpen ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
|
|
600
|
-
</
|
|
612
|
+
{instructionsOpen ? <ChevronDown size={14} aria-hidden /> : <ChevronRight size={14} aria-hidden />}
|
|
613
|
+
</button>
|
|
601
614
|
{instructionsOpen && instructions && (
|
|
602
|
-
<div className="mod-instructions-body">
|
|
615
|
+
<div id={`mod-instructions-body-${mod.id}`} className="mod-instructions-body">
|
|
603
616
|
{instructions.agents.length > 0 && (
|
|
604
617
|
<div className="mod-instructions-section">
|
|
605
618
|
<div className="mod-instructions-section-title">
|
|
@@ -288,11 +288,14 @@ function OverviewInner({
|
|
|
288
288
|
}
|
|
289
289
|
}}
|
|
290
290
|
>
|
|
291
|
+
<label htmlFor="overview-hero-input" className="sr-only">Describe what you want Odin to do</label>
|
|
291
292
|
<textarea
|
|
293
|
+
id="overview-hero-input"
|
|
292
294
|
ref={inputRef}
|
|
293
295
|
className="overview-input-hero"
|
|
294
296
|
placeholder="e.g. Implement user authentication with email + password, including registration, login, password reset, and integration tests. Use Bcrypt, JWT tokens, and the existing API style."
|
|
295
297
|
disabled={submitting}
|
|
298
|
+
aria-label="Describe what you want Odin to do"
|
|
296
299
|
/>
|
|
297
300
|
<div style={{ display: 'flex', gap: 'var(--space-2)', alignItems: 'center', flexWrap: 'wrap' }}>
|
|
298
301
|
<Button
|
|
@@ -367,7 +370,11 @@ function OverviewInner({
|
|
|
367
370
|
No activity yet. Use the chat above or invoke a Bizar command to start a feed.
|
|
368
371
|
</div>
|
|
369
372
|
) : (
|
|
370
|
-
<div
|
|
373
|
+
<div
|
|
374
|
+
className={cn('activity-feed-list-wrap', !activityExpanded && 'activity-feed-list-wrap-collapsed')}
|
|
375
|
+
aria-live="polite"
|
|
376
|
+
aria-relevant="additions"
|
|
377
|
+
>
|
|
371
378
|
<VirtualList
|
|
372
379
|
items={activityItems.slice(0, 30).filter((it, idx) => !hiddenKeys.has(itemKey(it, idx)))}
|
|
373
380
|
itemHeight={60}
|
|
@@ -75,14 +75,14 @@ export function Providers({ snapshot, settings, refreshSnapshot }: Props) {
|
|
|
75
75
|
title: 'Add Provider',
|
|
76
76
|
children: (
|
|
77
77
|
<div className="provider-form">
|
|
78
|
-
<label className="field-label">Provider name</label>
|
|
79
|
-
<input ref={(el) => { nameEl = el; }} className="input" type="text" placeholder="e.g. Anthropic, OpenAI" autoFocus />
|
|
80
|
-
<label className="field-label">Base URL</label>
|
|
81
|
-
<input ref={(el) => { urlEl = el; }} className="input" type="text" placeholder="https://api.openai.com/v1" />
|
|
82
|
-
<label className="field-label">API Key</label>
|
|
83
|
-
<input ref={(el) => { keyEl = el; }} className="input" type="password" placeholder="sk-..." />
|
|
84
|
-
<label className="field-label">Models (one per line)</label>
|
|
85
|
-
<textarea ref={(el) => { modelsEl = el; }} className="input" rows={4} placeholder="gpt-4o gpt-4o-mini ..." />
|
|
78
|
+
<label className="field-label" htmlFor="provider-new-name">Provider name</label>
|
|
79
|
+
<input id="provider-new-name" ref={(el) => { nameEl = el; }} className="input" type="text" placeholder="e.g. Anthropic, OpenAI" autoFocus />
|
|
80
|
+
<label className="field-label" htmlFor="provider-new-url">Base URL</label>
|
|
81
|
+
<input id="provider-new-url" ref={(el) => { urlEl = el; }} className="input" type="text" placeholder="https://api.openai.com/v1" />
|
|
82
|
+
<label className="field-label" htmlFor="provider-new-key">API Key</label>
|
|
83
|
+
<input id="provider-new-key" ref={(el) => { keyEl = el; }} className="input" type="password" placeholder="sk-..." />
|
|
84
|
+
<label className="field-label" htmlFor="provider-new-models">Models (one per line)</label>
|
|
85
|
+
<textarea id="provider-new-models" ref={(el) => { modelsEl = el; }} className="input" rows={4} placeholder="gpt-4o gpt-4o-mini ..." />
|
|
86
86
|
</div>
|
|
87
87
|
),
|
|
88
88
|
footer: (
|
|
@@ -118,14 +118,14 @@ export function Providers({ snapshot, settings, refreshSnapshot }: Props) {
|
|
|
118
118
|
title: `Edit "${provider.name}"`,
|
|
119
119
|
children: (
|
|
120
120
|
<div className="provider-form">
|
|
121
|
-
<label className="field-label">Provider name</label>
|
|
122
|
-
<input ref={(el) => { nameEl = el; }} className="input" type="text" defaultValue={provider.name} />
|
|
123
|
-
<label className="field-label">Base URL</label>
|
|
124
|
-
<input ref={(el) => { urlEl = el; }} className="input" type="text" defaultValue={provider.baseURL} />
|
|
125
|
-
<label className="field-label">API Key (leave blank to keep current)</label>
|
|
126
|
-
<input ref={(el) => { keyEl = el; }} className="input" type="password" placeholder="sk-..." />
|
|
127
|
-
<label className="field-label">Models (one per line)</label>
|
|
128
|
-
<textarea ref={(el) => { modelsEl = el; }} className="input" rows={4} defaultValue={(provider.models || []).join('\n')} />
|
|
121
|
+
<label className="field-label" htmlFor="provider-edit-name">Provider name</label>
|
|
122
|
+
<input id="provider-edit-name" ref={(el) => { nameEl = el; }} className="input" type="text" defaultValue={provider.name} />
|
|
123
|
+
<label className="field-label" htmlFor="provider-edit-url">Base URL</label>
|
|
124
|
+
<input id="provider-edit-url" ref={(el) => { urlEl = el; }} className="input" type="text" defaultValue={provider.baseURL} />
|
|
125
|
+
<label className="field-label" htmlFor="provider-edit-key">API Key (leave blank to keep current)</label>
|
|
126
|
+
<input id="provider-edit-key" ref={(el) => { keyEl = el; }} className="input" type="password" placeholder="sk-..." />
|
|
127
|
+
<label className="field-label" htmlFor="provider-edit-models">Models (one per line)</label>
|
|
128
|
+
<textarea id="provider-edit-models" ref={(el) => { modelsEl = el; }} className="input" rows={4} defaultValue={(provider.models || []).join('\n')} />
|
|
129
129
|
</div>
|
|
130
130
|
),
|
|
131
131
|
footer: (
|