@polderlabs/bizar 6.3.0 → 7.0.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/.claude/hooks/sessionend-recall.mjs +3 -3
- package/.claude/hooks/worker-suggest.mjs +110 -0
- package/.claude/settings.json +9 -0
- package/README.md +125 -47
- package/bizar-dash/dist/assets/{EnvVarsSection-B58aiJiE.js → EnvVarsSection-weVnLz3Z.js} +3 -3
- package/bizar-dash/dist/assets/EnvVarsSection-weVnLz3Z.js.map +1 -0
- package/bizar-dash/dist/assets/MobileChat-BRFjer8p.js +1 -0
- package/bizar-dash/dist/assets/{MobileChat-BJrqwVDd.js.map → MobileChat-BRFjer8p.js.map} +1 -1
- package/bizar-dash/dist/assets/MobileSettings-Dw5jyJWi.js +1 -0
- package/bizar-dash/dist/assets/MobileSettings-Dw5jyJWi.js.map +1 -0
- package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js → Toast-WTxc3m2g.js} +1 -1
- package/bizar-dash/dist/assets/{Toast-Cpl6-E63.js.map → Toast-WTxc3m2g.js.map} +1 -1
- package/bizar-dash/dist/assets/flow-BpU0-gK2.js +1 -0
- package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +1 -0
- package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js +1 -0
- package/bizar-dash/dist/assets/fuzzy-l0sNRNKZ.js.map +1 -0
- package/bizar-dash/dist/assets/{icons-Do5N2jmX.js → icons-B0AKnYI3.js} +98 -103
- package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +1 -0
- package/bizar-dash/dist/assets/main-BE4Uh9im.css +1 -0
- package/bizar-dash/dist/assets/main-DqC58FEZ.js +18 -0
- package/bizar-dash/dist/assets/main-DqC58FEZ.js.map +1 -0
- package/bizar-dash/dist/assets/markdown-w2bDDkAH.js +1 -0
- package/bizar-dash/dist/assets/markdown-w2bDDkAH.js.map +1 -0
- package/bizar-dash/dist/assets/mobile-DRZsK3ic.js +1 -0
- package/bizar-dash/dist/assets/{mobile-DYCHcUpq.js.map → mobile-DRZsK3ic.js.map} +1 -1
- package/bizar-dash/dist/assets/mobile-layout-BWLtwtfQ.css +1 -0
- package/bizar-dash/dist/assets/mobile-layout-Ckru3HKJ.js +2 -0
- package/bizar-dash/dist/assets/mobile-layout-Ckru3HKJ.js.map +1 -0
- package/bizar-dash/dist/assets/react-vendor-BnuhLJ6X.css +1 -0
- package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js +46 -0
- package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +1 -0
- package/bizar-dash/dist/assets/useSlashCommands-BAxVOU_a.js +2 -0
- package/bizar-dash/dist/assets/useSlashCommands-BAxVOU_a.js.map +1 -0
- package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js +45 -0
- package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +1 -0
- package/bizar-dash/dist/index.html +13 -11
- package/bizar-dash/dist/mobile.html +8 -5
- package/bizar-dash/src/server/api.mjs +11 -0
- package/bizar-dash/src/server/memory-consolidator.mjs +225 -0
- package/bizar-dash/src/server/memory-distillation.mjs +296 -0
- package/bizar-dash/src/server/routes/distill.mjs +129 -0
- package/bizar-dash/src/server/routes/goal-planner.mjs +236 -0
- package/bizar-dash/src/web/App.tsx +4 -0
- package/bizar-dash/src/web/components/Tag.tsx +18 -5
- package/bizar-dash/src/web/components/Topbar.tsx +3 -0
- package/bizar-dash/src/web/components/agents/CommunicationLog.tsx +126 -0
- package/bizar-dash/src/web/components/agents/DependencyGraph.tsx +163 -0
- package/bizar-dash/src/web/components/agents/QualityGates.tsx +123 -0
- package/bizar-dash/src/web/components/agents/RealTimeEventLog.tsx +127 -0
- package/bizar-dash/src/web/components/agents/RoutingDecisions.test.tsx +148 -0
- package/bizar-dash/src/web/components/agents/RoutingDecisions.tsx +160 -0
- package/bizar-dash/src/web/components/goals/GoalInput.tsx +86 -0
- package/bizar-dash/src/web/components/goals/PlanVisualization.tsx +111 -0
- package/bizar-dash/src/web/lib/goapPlanner.ts +614 -0
- package/bizar-dash/src/web/lib/types.ts +25 -0
- package/bizar-dash/src/web/mobile/MobileApp.tsx +33 -2
- package/bizar-dash/src/web/mobile/MobileBottomNav.tsx +2 -2
- package/bizar-dash/src/web/mobile/MobileEval.tsx +4 -4
- package/bizar-dash/src/web/mobile/MobileMemory.tsx +2 -2
- package/bizar-dash/src/web/mobile/MobileSettings.tsx +3 -3
- package/bizar-dash/src/web/mobile/MobileTasks.tsx +2 -2
- package/bizar-dash/src/web/mobile/components/MobileListItem.tsx +3 -3
- package/bizar-dash/src/web/styles/agents-redesign.css +311 -0
- package/bizar-dash/src/web/styles/main.css +251 -0
- package/bizar-dash/src/web/styles/mobile.css +41 -0
- package/bizar-dash/src/web/styles/tasks-redesign.css +253 -0
- package/bizar-dash/src/web/ui/controls/Button.tsx +77 -0
- package/bizar-dash/src/web/ui/controls/Checkbox.tsx +95 -0
- package/bizar-dash/src/web/ui/controls/IconButton.tsx +66 -0
- package/bizar-dash/src/web/ui/controls/Kbd.tsx +24 -0
- package/bizar-dash/src/web/ui/controls/NumberInput.tsx +77 -0
- package/bizar-dash/src/web/ui/controls/RadioGroup.tsx +109 -0
- package/bizar-dash/src/web/ui/controls/SearchInput.tsx +91 -0
- package/bizar-dash/src/web/ui/controls/Select.tsx +92 -0
- package/bizar-dash/src/web/ui/controls/Slider.tsx +70 -0
- package/bizar-dash/src/web/ui/controls/TextInput.tsx +67 -0
- package/bizar-dash/src/web/ui/controls/Textarea.tsx +56 -0
- package/bizar-dash/src/web/ui/controls/Toggle.tsx +79 -0
- package/bizar-dash/src/web/ui/controls/controls.css +476 -0
- package/bizar-dash/src/web/ui/controls/index.ts +35 -0
- package/bizar-dash/src/web/ui/data/BarChart.tsx +60 -0
- package/bizar-dash/src/web/ui/data/DataTable.tsx +216 -0
- package/bizar-dash/src/web/ui/data/EmptyState.tsx +60 -0
- package/bizar-dash/src/web/ui/data/ErrorState.tsx +56 -0
- package/bizar-dash/src/web/ui/data/KeyValueList.tsx +106 -0
- package/bizar-dash/src/web/ui/data/LoadingState.tsx +65 -0
- package/bizar-dash/src/web/ui/data/Sparkline.tsx +87 -0
- package/bizar-dash/src/web/ui/data/StatTile.tsx +87 -0
- package/bizar-dash/src/web/ui/data/data.css +535 -0
- package/bizar-dash/src/web/ui/data/index.ts +31 -0
- package/bizar-dash/src/web/ui/feedback/Badge.tsx +49 -0
- package/bizar-dash/src/web/ui/feedback/Dialog.tsx +209 -0
- package/bizar-dash/src/web/ui/feedback/ProgressBar.tsx +69 -0
- package/bizar-dash/src/web/ui/feedback/StatusDot.tsx +50 -0
- package/bizar-dash/src/web/ui/feedback/Toast.tsx +200 -0
- package/bizar-dash/src/web/ui/feedback/Tooltip.tsx +126 -0
- package/bizar-dash/src/web/ui/feedback/feedback.css +314 -0
- package/bizar-dash/src/web/ui/feedback/index.ts +43 -0
- package/bizar-dash/src/web/ui/index.ts +56 -0
- package/bizar-dash/src/web/ui/layout/AppShell.tsx +51 -0
- package/bizar-dash/src/web/ui/layout/Breadcrumbs.tsx +97 -0
- package/bizar-dash/src/web/ui/layout/Panel.tsx +73 -0
- package/bizar-dash/src/web/ui/layout/PanelHeader.tsx +39 -0
- package/bizar-dash/src/web/ui/layout/Sidebar.tsx +131 -0
- package/bizar-dash/src/web/ui/layout/Tabs.tsx +133 -0
- package/bizar-dash/src/web/ui/layout/Topbar.tsx +42 -0
- package/bizar-dash/src/web/ui/layout/ViewHeader.tsx +48 -0
- package/bizar-dash/src/web/ui/layout/index.ts +34 -0
- package/bizar-dash/src/web/ui/layout/layout.css +464 -0
- package/bizar-dash/src/web/ui/navigation/CommandPalette.tsx +199 -0
- package/bizar-dash/src/web/ui/navigation/NavGroup.tsx +95 -0
- package/bizar-dash/src/web/ui/navigation/NavLink.tsx +93 -0
- package/bizar-dash/src/web/ui/navigation/index.ts +17 -0
- package/bizar-dash/src/web/ui/navigation/navigation.css +343 -0
- package/bizar-dash/src/web/ui/primitives/Box.tsx +91 -0
- package/bizar-dash/src/web/ui/primitives/Grid.tsx +49 -0
- package/bizar-dash/src/web/ui/primitives/Inline.tsx +55 -0
- package/bizar-dash/src/web/ui/primitives/Separator.tsx +46 -0
- package/bizar-dash/src/web/ui/primitives/Stack.tsx +54 -0
- package/bizar-dash/src/web/ui/primitives/VisuallyHidden.tsx +26 -0
- package/bizar-dash/src/web/ui/primitives/index.ts +23 -0
- package/bizar-dash/src/web/ui/primitives/primitives.css +183 -0
- package/bizar-dash/src/web/ui/styles/globals.css +86 -0
- package/bizar-dash/src/web/ui/styles/reset.css +125 -0
- package/bizar-dash/src/web/ui/styles/tokens.css +208 -0
- package/bizar-dash/src/web/ui/theme/ThemeProvider.tsx +142 -0
- package/bizar-dash/src/web/ui/theme/tokens.ts +131 -0
- package/bizar-dash/src/web/ui/theme/useTheme.ts +22 -0
- package/bizar-dash/src/web/ui/utils/cx.ts +42 -0
- package/bizar-dash/src/web/views/Agents.tsx +516 -268
- package/bizar-dash/src/web/views/GoalPlanner.tsx +158 -0
- package/bizar-dash/src/web/views/Overview.tsx +985 -448
- package/bizar-dash/src/web/views/Tasks.tsx +271 -273
- package/bizar-dash/tests/communication-log.test.tsx +67 -0
- package/bizar-dash/tests/dep-graph.test.tsx +40 -0
- package/bizar-dash/tests/event-log.test.tsx +61 -0
- package/bizar-dash/tests/goal-input.test.tsx +72 -0
- package/bizar-dash/tests/goap-planner.test.ts +68 -0
- package/bizar-dash/tests/quality-gates.test.tsx +59 -0
- package/bizar-dash/tests/setup.ts +31 -1
- package/bizar-dash/tests/ui/controls/Button.test.tsx +50 -0
- package/bizar-dash/tests/ui/controls/Checkbox.test.tsx +44 -0
- package/bizar-dash/tests/ui/controls/IconButton.test.tsx +37 -0
- package/bizar-dash/tests/ui/controls/Kbd.test.tsx +39 -0
- package/bizar-dash/tests/ui/controls/NumberInput.test.tsx +56 -0
- package/bizar-dash/tests/ui/controls/RadioGroup.test.tsx +58 -0
- package/bizar-dash/tests/ui/controls/SearchInput.test.tsx +52 -0
- package/bizar-dash/tests/ui/controls/Select.test.tsx +66 -0
- package/bizar-dash/tests/ui/controls/Slider.test.tsx +41 -0
- package/bizar-dash/tests/ui/controls/TextInput.test.tsx +44 -0
- package/bizar-dash/tests/ui/controls/Textarea.test.tsx +56 -0
- package/bizar-dash/tests/ui/controls/Toggle.test.tsx +39 -0
- package/bizar-dash/tests/ui/data/BarChart.test.tsx +55 -0
- package/bizar-dash/tests/ui/data/DataTable.test.tsx +112 -0
- package/bizar-dash/tests/ui/data/EmptyState.test.tsx +57 -0
- package/bizar-dash/tests/ui/data/ErrorState.test.tsx +47 -0
- package/bizar-dash/tests/ui/data/KeyValueList.test.tsx +59 -0
- package/bizar-dash/tests/ui/data/LoadingState.test.tsx +37 -0
- package/bizar-dash/tests/ui/data/Sparkline.test.tsx +56 -0
- package/bizar-dash/tests/ui/data/StatTile.test.tsx +69 -0
- package/bizar-dash/tests/ui/feedback/Badge.test.tsx +41 -0
- package/bizar-dash/tests/ui/feedback/Dialog.test.tsx +99 -0
- package/bizar-dash/tests/ui/feedback/ProgressBar.test.tsx +66 -0
- package/bizar-dash/tests/ui/feedback/StatusDot.test.tsx +41 -0
- package/bizar-dash/tests/ui/feedback/Toast.test.tsx +124 -0
- package/bizar-dash/tests/ui/feedback/Tooltip.test.tsx +102 -0
- package/bizar-dash/tests/ui/layout/AppShell.test.tsx +50 -0
- package/bizar-dash/tests/ui/layout/Breadcrumbs.test.tsx +85 -0
- package/bizar-dash/tests/ui/layout/Panel.test.tsx +52 -0
- package/bizar-dash/tests/ui/layout/PanelHeader.test.tsx +32 -0
- package/bizar-dash/tests/ui/layout/Sidebar.test.tsx +95 -0
- package/bizar-dash/tests/ui/layout/Tabs.test.tsx +87 -0
- package/bizar-dash/tests/ui/layout/Topbar.test.tsx +41 -0
- package/bizar-dash/tests/ui/layout/ViewHeader.test.tsx +47 -0
- package/bizar-dash/tests/ui/navigation/CommandPalette.test.tsx +105 -0
- package/bizar-dash/tests/ui/navigation/NavGroup.test.tsx +55 -0
- package/bizar-dash/tests/ui/navigation/NavLink.test.tsx +61 -0
- package/bizar-dash/tests/ui/primitives/Box.test.tsx +49 -0
- package/bizar-dash/tests/ui/primitives/Grid.test.tsx +37 -0
- package/bizar-dash/tests/ui/primitives/Inline.test.tsx +33 -0
- package/bizar-dash/tests/ui/primitives/Separator.test.tsx +34 -0
- package/bizar-dash/tests/ui/primitives/Stack.test.tsx +45 -0
- package/bizar-dash/tests/ui/primitives/VisuallyHidden.test.tsx +27 -0
- package/bizar-dash/tests/views/Agents.test.tsx +359 -0
- package/bizar-dash/tests/views/Overview.test.tsx +413 -0
- package/bizar-dash/tests/views/Tasks.test.tsx +250 -0
- package/bizar-dash/vitest.config.ts +3 -1
- package/cli/__tests__/cost-gate.test.mjs +301 -0
- package/cli/__tests__/feature-list-bridge.test.mjs +371 -0
- package/cli/bin.mjs +38 -0
- package/cli/commands/claim.mjs +282 -0
- package/cli/commands/cost.mjs +279 -0
- package/cli/cost-gate.mjs +501 -0
- package/cli/feature-list-bridge.mjs +400 -0
- package/cli/memory-constants.mjs +17 -0
- package/cli/provision-claude.mjs +94 -0
- package/cli/worker-dispatcher.mjs +259 -0
- package/cli/worker-dispatcher.test.mjs +204 -0
- package/config/trigger-patterns.json +186 -0
- package/package.json +2 -2
- package/packages/sdk/package.json +1 -1
- package/packages/sdk/src/agent-registry.ts +304 -0
- package/packages/sdk/src/consensus/byzantine.ts +587 -0
- package/packages/sdk/src/consensus/index.ts +147 -0
- package/packages/sdk/src/consensus/queen.ts +173 -0
- package/packages/sdk/src/consensus/types.ts +199 -0
- package/packages/sdk/src/federation/audit.ts +156 -0
- package/packages/sdk/src/federation/budget.ts +269 -0
- package/packages/sdk/src/federation/envelope.ts +144 -0
- package/packages/sdk/src/federation/hmac.ts +160 -0
- package/packages/sdk/src/federation/index.ts +443 -0
- package/packages/sdk/src/federation/pii.ts +241 -0
- package/packages/sdk/src/federation/policy.ts +112 -0
- package/packages/sdk/src/federation/trust.ts +146 -0
- package/packages/sdk/src/index.ts +67 -0
- package/packages/sdk/src/mcp/server.ts +351 -1
- package/packages/sdk/src/router/codemod-intent.ts +125 -0
- package/packages/sdk/src/router/index.ts +247 -0
- package/packages/sdk/src/router/memory-distillation-shim.mjs +18 -0
- package/packages/sdk/src/router/memory-distillation.ts +249 -0
- package/packages/sdk/src/router/model-router.ts +235 -0
- package/packages/sdk/src/router/q-learning-router.ts +304 -0
- package/packages/sdk/src/swarm-topology.ts +279 -0
- package/packages/sdk/tests/agent-registry.test.ts +285 -0
- package/packages/sdk/tests/consensus.test.ts +376 -0
- package/packages/sdk/tests/federation/audit.test.ts +164 -0
- package/packages/sdk/tests/federation/budget.test.ts +189 -0
- package/packages/sdk/tests/federation/envelope.test.ts +120 -0
- package/packages/sdk/tests/federation/hmac.test.ts +194 -0
- package/packages/sdk/tests/federation/orchestrator.test.ts +176 -0
- package/packages/sdk/tests/federation/pii.test.ts +106 -0
- package/packages/sdk/tests/federation/policy.test.ts +140 -0
- package/packages/sdk/tests/federation/trust.test.ts +124 -0
- package/packages/sdk/tests/mcp-tools.test.ts +204 -0
- package/packages/sdk/tests/memory-distillation.test.mjs +186 -0
- package/packages/sdk/tests/model-router.test.mjs +108 -0
- package/packages/sdk/tests/q-learning-router.test.mjs +100 -0
- package/packages/sdk/tests/router-orchestrator.test.mjs +172 -0
- package/packages/sdk/tests/router.test.mjs +91 -0
- package/packages/sdk/tests/sdk.test.mjs +26 -2
- package/packages/sdk/tests/swarm-topology.test.ts +258 -0
- package/scripts/bh-full-e2e.mjs +11 -15
- package/bizar-dash/dist/assets/EnvVarsSection-B58aiJiE.js.map +0 -1
- package/bizar-dash/dist/assets/MobileChat-BJrqwVDd.js +0 -1
- package/bizar-dash/dist/assets/MobileSettings-CEQNJNLJ.js +0 -1
- package/bizar-dash/dist/assets/MobileSettings-CEQNJNLJ.js.map +0 -1
- package/bizar-dash/dist/assets/icons-Do5N2jmX.js.map +0 -1
- package/bizar-dash/dist/assets/main-IvfQAOfy.js +0 -18
- package/bizar-dash/dist/assets/main-IvfQAOfy.js.map +0 -1
- package/bizar-dash/dist/assets/main-ietCEg_R.css +0 -1
- package/bizar-dash/dist/assets/markdown-tOLaD6nm.js +0 -1
- package/bizar-dash/dist/assets/markdown-tOLaD6nm.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-DYCHcUpq.js +0 -1
- package/bizar-dash/dist/assets/mobile-layout-CBHjpwsb.js +0 -2
- package/bizar-dash/dist/assets/mobile-layout-CBHjpwsb.js.map +0 -1
- package/bizar-dash/dist/assets/mobile-layout-CJnZNLy3.css +0 -1
- package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js +0 -40
- package/bizar-dash/dist/assets/react-vendor-Dn4wqh4Z.js.map +0 -1
- package/bizar-dash/dist/assets/useSlashCommands-Bd7_FA6U.js +0 -2
- package/bizar-dash/dist/assets/useSlashCommands-Bd7_FA6U.js.map +0 -1
- package/bizar-dash/dist/assets/vendor-C843201K.js +0 -29
- package/bizar-dash/dist/assets/vendor-C843201K.js.map +0 -1
- package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
- package/packages/sdk/package-lock.json +0 -32
|
@@ -1,55 +1,73 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
/*
|
|
2
|
+
* Tasks.tsx — Wave 3 redesigned kanban board (Supabase-style simplicity).
|
|
3
|
+
*
|
|
4
|
+
* The view groups tasks into 5 columns by API status (queued | doing | done
|
|
5
|
+
* | blocked | backlog) and renders each column as a `Panel` from the new
|
|
6
|
+
* design system. Task rows are compact (~76px) cards with a left-edge
|
|
7
|
+
* priority accent (3px) derived from the priority token. The redesign
|
|
8
|
+
* intentionally uses only design-system primitives (Panel, Grid, Stack,
|
|
9
|
+
* Inline, Badge, StatusDot, Button, IconButton, SearchInput, Select,
|
|
10
|
+
* EmptyState, LoadingState) — no legacy `.task-card` / `.kanban-column`
|
|
11
|
+
* classes from main.css.
|
|
12
|
+
*
|
|
13
|
+
* Hard constraints preserved from the previous implementation:
|
|
14
|
+
* - Same export name (`Tasks`) and same `Props` shape so App.tsx keeps
|
|
15
|
+
* importing without changes.
|
|
16
|
+
* - Same API contracts (`api.get('/tasks')`, `api.patch(.../status)`, etc).
|
|
17
|
+
* - Same WS subscriptions (`tasks:change`, `tasks:delete`).
|
|
18
|
+
* - Same COLUMNS array — the API status enum is the public contract.
|
|
19
|
+
* - BacklogPanel is still imported from `components/tasks/BacklogPanel`
|
|
20
|
+
* and receives the same props (out-of-scope for this redesign).
|
|
21
|
+
* - Legacy `Modal` is reused for the create/edit dialogs (out-of-scope).
|
|
22
|
+
* - 30s tick for relative-time labels.
|
|
23
|
+
*
|
|
24
|
+
* What changed:
|
|
25
|
+
* - Columns are now `Panel`s with a StatusDot + count in the header.
|
|
26
|
+
* - Task rows render with `Badge` for status/priority/agent and
|
|
27
|
+
* `IconButton` for actions (edit, submit-to-odin, retry, delete).
|
|
28
|
+
* - The whole task row is clickable (delegates to the edit modal) —
|
|
29
|
+
* action buttons stop propagation so they keep their dedicated path.
|
|
30
|
+
* - Toolbar uses `SearchInput` + `Select` (priority) instead of raw
|
|
31
|
+
* inputs.
|
|
32
|
+
*/
|
|
23
33
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
24
34
|
import {
|
|
25
35
|
CheckSquare,
|
|
26
36
|
Plus,
|
|
27
37
|
Trash2,
|
|
28
|
-
|
|
29
|
-
ChevronRight,
|
|
30
|
-
Search,
|
|
31
|
-
X as XIcon,
|
|
32
|
-
Clock,
|
|
38
|
+
RefreshCw,
|
|
33
39
|
Tag as TagIcon,
|
|
34
40
|
ArchiveRestore,
|
|
35
|
-
|
|
41
|
+
RotateCw,
|
|
36
42
|
Send,
|
|
37
43
|
Inbox,
|
|
38
|
-
Sparkles,
|
|
39
44
|
Edit2,
|
|
40
|
-
RotateCw,
|
|
41
45
|
Bot,
|
|
42
46
|
} from 'lucide-react';
|
|
43
47
|
import { Button } from '../components/Button';
|
|
44
48
|
import { Card, CardTitle, CardMeta } from '../components/Card';
|
|
45
|
-
import { Spinner } from '../components/Spinner';
|
|
46
|
-
import { Tag } from '../components/Tag';
|
|
47
49
|
import { useModal } from '../components/Modal';
|
|
48
50
|
import { useToast } from '../components/Toast';
|
|
49
51
|
import { api, enhancePrompt } from '../lib/api';
|
|
50
|
-
import {
|
|
52
|
+
import { formatRelative } from '../lib/utils';
|
|
51
53
|
import type { Settings, Snapshot, Task } from '../lib/types';
|
|
52
54
|
import { BacklogPanel } from '../components/tasks/BacklogPanel';
|
|
55
|
+
import {
|
|
56
|
+
Badge,
|
|
57
|
+
cx,
|
|
58
|
+
Grid,
|
|
59
|
+
IconButton,
|
|
60
|
+
Inline,
|
|
61
|
+
LoadingState,
|
|
62
|
+
Panel,
|
|
63
|
+
SearchInput,
|
|
64
|
+
Select,
|
|
65
|
+
Stack,
|
|
66
|
+
StatusDot,
|
|
67
|
+
type BadgeVariant,
|
|
68
|
+
} from '../ui';
|
|
69
|
+
|
|
70
|
+
import '../styles/tasks-redesign.css';
|
|
53
71
|
|
|
54
72
|
type Props = {
|
|
55
73
|
snapshot: Snapshot;
|
|
@@ -60,6 +78,7 @@ type Props = {
|
|
|
60
78
|
};
|
|
61
79
|
|
|
62
80
|
type StatusKind = 'info' | 'accent' | 'success' | 'warning' | 'error';
|
|
81
|
+
type PriorityKind = 'low' | 'normal' | 'high';
|
|
63
82
|
|
|
64
83
|
/**
|
|
65
84
|
* UI <-> API status mapping. We keep the existing API enum
|
|
@@ -67,17 +86,43 @@ type StatusKind = 'info' | 'accent' | 'success' | 'warning' | 'error';
|
|
|
67
86
|
* rename the labels here so the dashboard, server, and tests stay in
|
|
68
87
|
* sync. Switch back if you need to expose the raw API value.
|
|
69
88
|
*/
|
|
70
|
-
const COLUMNS: {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
89
|
+
const COLUMNS: {
|
|
90
|
+
id: Task['status'];
|
|
91
|
+
label: string;
|
|
92
|
+
kind: StatusKind;
|
|
93
|
+
dotVariant: 'info' | 'success' | 'warning' | 'danger';
|
|
94
|
+
}[] = [
|
|
95
|
+
{ id: 'queued', label: 'Todo', kind: 'info', dotVariant: 'info' },
|
|
96
|
+
{ id: 'doing', label: 'In progress', kind: 'accent', dotVariant: 'info' },
|
|
97
|
+
{ id: 'done', label: 'Done', kind: 'success', dotVariant: 'success' },
|
|
98
|
+
{ id: 'blocked', label: 'Failed', kind: 'error', dotVariant: 'danger' },
|
|
75
99
|
];
|
|
76
100
|
|
|
77
|
-
const PRIORITIES = ['low', 'normal', 'high']
|
|
78
|
-
|
|
101
|
+
const PRIORITIES: readonly PriorityKind[] = ['low', 'normal', 'high'];
|
|
102
|
+
|
|
103
|
+
const PRIORITY_OPTIONS = [
|
|
104
|
+
{ value: '', label: 'All' },
|
|
105
|
+
...PRIORITIES.map((p) => ({ value: p, label: p })),
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
const STATUS_BADGE: Record<StatusKind, BadgeVariant> = {
|
|
109
|
+
info: 'info',
|
|
110
|
+
accent: 'accent',
|
|
111
|
+
success: 'success',
|
|
112
|
+
warning: 'warning',
|
|
113
|
+
error: 'danger',
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const PRIORITY_BADGE: Record<PriorityKind, BadgeVariant> = {
|
|
117
|
+
high: 'danger',
|
|
118
|
+
normal: 'info',
|
|
119
|
+
low: 'neutral',
|
|
120
|
+
};
|
|
79
121
|
|
|
80
|
-
function TasksInner({
|
|
122
|
+
function TasksInner({
|
|
123
|
+
snapshot,
|
|
124
|
+
refreshSnapshot,
|
|
125
|
+
}: Props) {
|
|
81
126
|
const toast = useToast();
|
|
82
127
|
const modal = useModal();
|
|
83
128
|
const [tasks, setTasks] = useState<Task[]>(snapshot.tasks || []);
|
|
@@ -219,72 +264,63 @@ function TasksInner({ snapshot, refreshSnapshot, setActiveTab }: Props) {
|
|
|
219
264
|
}
|
|
220
265
|
};
|
|
221
266
|
|
|
267
|
+
const handleTaskClick = (task: Task) => {
|
|
268
|
+
openEditTaskModal(modal, toast, task, setTasks, reload, refreshSnapshot);
|
|
269
|
+
};
|
|
270
|
+
|
|
222
271
|
return (
|
|
223
|
-
<div className="view view-tasks">
|
|
272
|
+
<div className="view view-tasks tasks-wave3">
|
|
224
273
|
{/* Hidden live region for screen-reader announcements of status changes. */}
|
|
225
274
|
<div className="sr-only" role="status" aria-live="polite">
|
|
226
275
|
{statusAnnouncement}
|
|
227
276
|
</div>
|
|
228
|
-
<header className="
|
|
229
|
-
<div
|
|
230
|
-
<h2 className="
|
|
231
|
-
<CheckSquare size={18} /> Tasks ({sorted.length})
|
|
277
|
+
<header className="tasks-wave3__header">
|
|
278
|
+
<div>
|
|
279
|
+
<h2 className="tasks-wave3__title-row">
|
|
280
|
+
<CheckSquare size={18} aria-hidden /> Tasks ({sorted.length})
|
|
232
281
|
</h2>
|
|
233
|
-
<p className="
|
|
282
|
+
<p className="tasks-wave3__subtitle">
|
|
234
283
|
Add a task — title and description is all you need. Odin picks the agent and priority.
|
|
235
284
|
</p>
|
|
236
285
|
</div>
|
|
237
286
|
</header>
|
|
238
287
|
|
|
239
|
-
{/* Toolbar: search, filter,
|
|
240
|
-
<div className="tasks-toolbar">
|
|
241
|
-
<div className="tasks-
|
|
242
|
-
<
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
onChange={(e) => setFilter(e.target.value)}
|
|
250
|
-
aria-label="Search tasks"
|
|
251
|
-
/>
|
|
252
|
-
{filter && (
|
|
253
|
-
<button
|
|
254
|
-
type="button"
|
|
255
|
-
className="icon-btn"
|
|
256
|
-
aria-label="Clear search"
|
|
257
|
-
onClick={() => setFilter('')}
|
|
258
|
-
>
|
|
259
|
-
<XIcon size={12} />
|
|
260
|
-
</button>
|
|
261
|
-
)}
|
|
262
|
-
</div>
|
|
288
|
+
{/* Toolbar: search, priority filter, refresh, backlog toggle, new task */}
|
|
289
|
+
<div className="tasks-wave3__toolbar" role="toolbar" aria-label="Task filters">
|
|
290
|
+
<div className="tasks-wave3__toolbar-group">
|
|
291
|
+
<SearchInput
|
|
292
|
+
inputSize="sm"
|
|
293
|
+
value={filter}
|
|
294
|
+
onChange={((v: unknown) => setFilter(String(v))) as unknown as React.ComponentProps<typeof SearchInput>['onChange']}
|
|
295
|
+
placeholder="Search…"
|
|
296
|
+
aria-label="Search tasks"
|
|
297
|
+
/>
|
|
263
298
|
</div>
|
|
264
299
|
|
|
265
|
-
<div className="tasks-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
<
|
|
270
|
-
|
|
271
|
-
|
|
300
|
+
<div className="tasks-wave3__toolbar-group">
|
|
301
|
+
<span className="tasks-wave3__toolbar-label" id="tasks-priority-filter-label">
|
|
302
|
+
Priority
|
|
303
|
+
</span>
|
|
304
|
+
<Select
|
|
305
|
+
inputSize="sm"
|
|
306
|
+
aria-labelledby="tasks-priority-filter-label"
|
|
307
|
+
options={PRIORITY_OPTIONS}
|
|
272
308
|
value={priorityFilter}
|
|
273
|
-
onChange={(e) => setPriorityFilter(e.target.value)}
|
|
274
|
-
|
|
275
|
-
>
|
|
276
|
-
<option value="">All</option>
|
|
277
|
-
{PRIORITIES.map((p) => (
|
|
278
|
-
<option key={p} value={p}>{p}</option>
|
|
279
|
-
))}
|
|
280
|
-
</select>
|
|
309
|
+
onChange={(e) => setPriorityFilter((e.target as HTMLSelectElement).value)}
|
|
310
|
+
/>
|
|
281
311
|
</div>
|
|
282
312
|
|
|
283
|
-
<div className="tasks-
|
|
313
|
+
<div className="tasks-wave3__toolbar-spacer" />
|
|
284
314
|
|
|
285
|
-
<div className="tasks-
|
|
286
|
-
<Button
|
|
287
|
-
|
|
315
|
+
<div className="tasks-wave3__toolbar-group">
|
|
316
|
+
<Button
|
|
317
|
+
variant="ghost"
|
|
318
|
+
size="sm"
|
|
319
|
+
onClick={reload}
|
|
320
|
+
title="Refresh"
|
|
321
|
+
aria-label="Refresh tasks"
|
|
322
|
+
>
|
|
323
|
+
<RefreshCw size={14} aria-hidden />
|
|
288
324
|
</Button>
|
|
289
325
|
{backlogCount > 0 && (
|
|
290
326
|
<Button
|
|
@@ -293,9 +329,11 @@ function TasksInner({ snapshot, refreshSnapshot, setActiveTab }: Props) {
|
|
|
293
329
|
onClick={() => setShowBacklog((v) => !v)}
|
|
294
330
|
title={showBacklog ? 'Hide backlog' : 'Show backlog'}
|
|
295
331
|
>
|
|
296
|
-
<Inbox size={14} />
|
|
332
|
+
<Inbox size={14} aria-hidden />
|
|
297
333
|
Backlog
|
|
298
|
-
<span className="
|
|
334
|
+
<span className="tasks-wave3__column-count" aria-label={`${backlogCount} items`}>
|
|
335
|
+
{backlogCount}
|
|
336
|
+
</span>
|
|
299
337
|
</Button>
|
|
300
338
|
)}
|
|
301
339
|
<Button
|
|
@@ -303,7 +341,7 @@ function TasksInner({ snapshot, refreshSnapshot, setActiveTab }: Props) {
|
|
|
303
341
|
size="sm"
|
|
304
342
|
onClick={() => openCreateTaskModal(modal, toast, setTasks, reload, refreshSnapshot)}
|
|
305
343
|
>
|
|
306
|
-
<Plus size={14} /> New task
|
|
344
|
+
<Plus size={14} aria-hidden /> New task
|
|
307
345
|
</Button>
|
|
308
346
|
</div>
|
|
309
347
|
</div>
|
|
@@ -316,218 +354,167 @@ function TasksInner({ snapshot, refreshSnapshot, setActiveTab }: Props) {
|
|
|
316
354
|
)}
|
|
317
355
|
|
|
318
356
|
{loading ? (
|
|
319
|
-
<
|
|
357
|
+
<LoadingState label="Loading tasks…" />
|
|
320
358
|
) : (
|
|
321
|
-
<
|
|
322
|
-
{COLUMNS.map((col) =>
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
359
|
+
<Grid cols={5} gap={3} className="tasks-wave3__kanban">
|
|
360
|
+
{COLUMNS.map((col) => {
|
|
361
|
+
const columnTasks = sorted.filter((t) => t.status === col.id);
|
|
362
|
+
return (
|
|
363
|
+
<Panel
|
|
364
|
+
key={col.id}
|
|
365
|
+
padding={3}
|
|
366
|
+
className="tasks-wave3__column"
|
|
367
|
+
title={
|
|
368
|
+
<span className="tasks-wave3__column-title">
|
|
369
|
+
<StatusDot variant={col.dotVariant} size="sm" label={col.label} />
|
|
370
|
+
{col.label}
|
|
371
|
+
</span>
|
|
372
|
+
}
|
|
373
|
+
actions={
|
|
374
|
+
<span
|
|
375
|
+
className="tasks-wave3__column-count"
|
|
376
|
+
aria-label={`${columnTasks.length} tasks`}
|
|
377
|
+
>
|
|
378
|
+
{columnTasks.length}
|
|
379
|
+
</span>
|
|
380
|
+
}
|
|
381
|
+
>
|
|
382
|
+
<Stack gap={2} className="tasks-wave3__column-body">
|
|
383
|
+
{columnTasks.length === 0 ? (
|
|
384
|
+
<div className="tasks-wave3__column-empty">No tasks</div>
|
|
385
|
+
) : (
|
|
386
|
+
columnTasks.map((t) => (
|
|
387
|
+
<TaskRow
|
|
388
|
+
key={t.id}
|
|
389
|
+
task={t}
|
|
390
|
+
column={col}
|
|
391
|
+
onTaskClick={handleTaskClick}
|
|
392
|
+
onEdit={() => openEditTaskModal(modal, toast, t, setTasks, reload, refreshSnapshot)}
|
|
393
|
+
onDelete={() => deleteTask(t.id)}
|
|
394
|
+
onRetry={() => retryTask(t.id)}
|
|
395
|
+
onSubmitToOdin={() => submitToOdin(t.id)}
|
|
396
|
+
tick={tick}
|
|
397
|
+
/>
|
|
398
|
+
))
|
|
399
|
+
)}
|
|
400
|
+
</Stack>
|
|
401
|
+
</Panel>
|
|
402
|
+
);
|
|
403
|
+
})}
|
|
404
|
+
</Grid>
|
|
336
405
|
)}
|
|
337
406
|
</div>
|
|
338
407
|
);
|
|
339
408
|
}
|
|
340
409
|
|
|
341
|
-
// ───
|
|
410
|
+
// ─── Row ─────────────────────────────────────────────────────────────────
|
|
342
411
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
onSubmitToOdin
|
|
351
|
-
tick,
|
|
352
|
-
}: {
|
|
353
|
-
column: { id: string; label: string; kind: StatusKind };
|
|
354
|
-
tasks: Task[];
|
|
355
|
-
onMove: (id: string, status: string) => void;
|
|
356
|
-
onDelete: (id: string) => void;
|
|
357
|
-
onRetry: (id: string) => void;
|
|
358
|
-
onEdit: (t: Task) => void;
|
|
359
|
-
onSubmitToOdin: (id: string) => void;
|
|
412
|
+
type RowProps = {
|
|
413
|
+
task: Task;
|
|
414
|
+
column: (typeof COLUMNS)[number];
|
|
415
|
+
onTaskClick: (task: Task) => void;
|
|
416
|
+
onEdit: () => void;
|
|
417
|
+
onDelete: () => void;
|
|
418
|
+
onRetry: () => void;
|
|
419
|
+
onSubmitToOdin: () => void;
|
|
360
420
|
tick: number;
|
|
361
|
-
}
|
|
362
|
-
const [dragOver, setDragOver] = useState(false);
|
|
363
|
-
return (
|
|
364
|
-
<div
|
|
365
|
-
className={cn('kanban-column', dragOver && 'kanban-column-drop')}
|
|
366
|
-
data-column={column.id}
|
|
367
|
-
onDragOver={(e) => { e.preventDefault(); setDragOver(true); }}
|
|
368
|
-
onDragLeave={() => setDragOver(false)}
|
|
369
|
-
onDrop={(e) => {
|
|
370
|
-
e.preventDefault();
|
|
371
|
-
setDragOver(false);
|
|
372
|
-
const id = e.dataTransfer.getData('text/task-id');
|
|
373
|
-
if (id) onMove(id, column.id);
|
|
374
|
-
}}
|
|
375
|
-
>
|
|
376
|
-
<div className="kanban-col-header">
|
|
377
|
-
<CardTitle>
|
|
378
|
-
<span className={cn('status-dot', `status-${column.kind}`)} />
|
|
379
|
-
{column.label}
|
|
380
|
-
</CardTitle>
|
|
381
|
-
<span className="kanban-col-count tabular-nums">{tasks.length}</span>
|
|
382
|
-
</div>
|
|
383
|
-
<div className="kanban-col-body">
|
|
384
|
-
{tasks.length === 0 ? (
|
|
385
|
-
<div className="kanban-empty">No tasks</div>
|
|
386
|
-
) : (
|
|
387
|
-
tasks.map((t) => (
|
|
388
|
-
<TaskCard
|
|
389
|
-
key={t.id}
|
|
390
|
-
task={t}
|
|
391
|
-
onMove={(dir) => {
|
|
392
|
-
const idx = COLUMNS.findIndex((c) => c.id === t.status);
|
|
393
|
-
const next = idx + dir;
|
|
394
|
-
if (next >= 0 && next < COLUMNS.length) onMove(t.id, COLUMNS[next].id);
|
|
395
|
-
}}
|
|
396
|
-
onEdit={() => onEdit(t)}
|
|
397
|
-
onDelete={() => onDelete(t.id)}
|
|
398
|
-
onRetry={() => onRetry(t.id)}
|
|
399
|
-
onSubmitToOdin={() => onSubmitToOdin(t.id)}
|
|
400
|
-
tick={tick}
|
|
401
|
-
/>
|
|
402
|
-
))
|
|
403
|
-
)}
|
|
404
|
-
</div>
|
|
405
|
-
</div>
|
|
406
|
-
);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// ─── Card ──────────────────────────────────────────────────────────────────
|
|
421
|
+
};
|
|
410
422
|
|
|
411
|
-
function
|
|
423
|
+
function TaskRow({
|
|
412
424
|
task,
|
|
413
|
-
|
|
425
|
+
column,
|
|
426
|
+
onTaskClick,
|
|
414
427
|
onEdit,
|
|
415
428
|
onDelete,
|
|
416
429
|
onRetry,
|
|
417
430
|
onSubmitToOdin,
|
|
418
431
|
tick,
|
|
419
|
-
}: {
|
|
420
|
-
task: Task;
|
|
421
|
-
onMove: (dir: -1 | 1) => void;
|
|
422
|
-
onEdit: () => void;
|
|
423
|
-
onDelete: () => void;
|
|
424
|
-
onRetry: () => void;
|
|
425
|
-
onSubmitToOdin: () => void;
|
|
426
|
-
tick: number;
|
|
427
|
-
}) {
|
|
432
|
+
}: RowProps) {
|
|
428
433
|
const assignedAgent = task.workedBy || task.assignee || null;
|
|
434
|
+
const priority: PriorityKind = (PRIORITIES as readonly string[]).includes(task.priority as string)
|
|
435
|
+
? (task.priority as PriorityKind)
|
|
436
|
+
: 'normal';
|
|
429
437
|
return (
|
|
430
438
|
<div
|
|
431
|
-
className={
|
|
439
|
+
className={cx('tasks-wave3__row')}
|
|
432
440
|
data-task-id={task.id}
|
|
433
|
-
draggable
|
|
434
|
-
onDragStart={(e) => {
|
|
435
|
-
e.dataTransfer.setData('text/task-id', task.id);
|
|
436
|
-
e.dataTransfer.effectAllowed = 'move';
|
|
437
|
-
}}
|
|
438
441
|
data-tick={tick}
|
|
442
|
+
role="button"
|
|
443
|
+
tabIndex={0}
|
|
444
|
+
onClick={() => onTaskClick(task)}
|
|
445
|
+
onKeyDown={(e) => {
|
|
446
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
447
|
+
e.preventDefault();
|
|
448
|
+
onTaskClick(task);
|
|
449
|
+
}
|
|
450
|
+
}}
|
|
439
451
|
>
|
|
440
|
-
<div className="
|
|
441
|
-
<
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
452
|
+
<div className="tasks-wave3__row-title-row">
|
|
453
|
+
<div className="tasks-wave3__row-title" title={task.title}>
|
|
454
|
+
{task.title}
|
|
455
|
+
</div>
|
|
456
|
+
<Badge variant={STATUS_BADGE[column.kind]} size="sm" aria-label={`Status ${column.label}`}>
|
|
457
|
+
{column.label}
|
|
458
|
+
</Badge>
|
|
446
459
|
</div>
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
460
|
+
|
|
461
|
+
<Inline gap={1} align="center" className="tasks-wave3__row-badges">
|
|
462
|
+
<Badge variant={PRIORITY_BADGE[priority]} size="sm">
|
|
463
|
+
{priority}
|
|
464
|
+
</Badge>
|
|
451
465
|
{assignedAgent && (
|
|
452
|
-
<
|
|
453
|
-
<Bot size={10} /> @{assignedAgent}
|
|
454
|
-
</
|
|
466
|
+
<Badge variant="neutral" size="sm">
|
|
467
|
+
<Bot size={10} aria-hidden /> @{assignedAgent}
|
|
468
|
+
</Badge>
|
|
455
469
|
)}
|
|
456
|
-
{task.
|
|
457
|
-
<
|
|
458
|
-
|
|
459
|
-
</
|
|
460
|
-
) : null}
|
|
461
|
-
{task.tags && task.tags.length > 0 && task.tags.slice(0, 3).map((tag) => (
|
|
462
|
-
<Tag key={tag}>{tag}</Tag>
|
|
470
|
+
{(task.tags || []).slice(0, 2).map((tag) => (
|
|
471
|
+
<Badge key={tag} variant="neutral" size="sm">
|
|
472
|
+
#{tag}
|
|
473
|
+
</Badge>
|
|
463
474
|
))}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
aria-label="Move left"
|
|
480
|
-
title="Move left"
|
|
481
|
-
onClick={() => onMove(-1)}
|
|
482
|
-
>
|
|
483
|
-
<ChevronLeft size={14} />
|
|
484
|
-
</button>
|
|
485
|
-
<button
|
|
486
|
-
type="button"
|
|
487
|
-
className="icon-btn"
|
|
488
|
-
aria-label="Edit"
|
|
475
|
+
</Inline>
|
|
476
|
+
|
|
477
|
+
<div className="tasks-wave3__row-footer">
|
|
478
|
+
<span className="tasks-wave3__row-time">{formatRelative(task.createdAt)}</span>
|
|
479
|
+
<Inline
|
|
480
|
+
gap={1}
|
|
481
|
+
align="center"
|
|
482
|
+
className="tasks-wave3__row-actions"
|
|
483
|
+
onClick={(e) => e.stopPropagation()}
|
|
484
|
+
>
|
|
485
|
+
<IconButton
|
|
486
|
+
variant="ghost"
|
|
487
|
+
size="sm"
|
|
488
|
+
icon={<Edit2 size={12} aria-hidden />}
|
|
489
|
+
aria-label="Edit task"
|
|
489
490
|
title="Edit"
|
|
490
491
|
onClick={onEdit}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
className="icon-btn"
|
|
492
|
+
/>
|
|
493
|
+
<IconButton
|
|
494
|
+
variant="ghost"
|
|
495
|
+
size="sm"
|
|
496
|
+
icon={<Send size={12} aria-hidden />}
|
|
497
497
|
aria-label="Submit to Odin"
|
|
498
498
|
title="Re-delegate to Odin"
|
|
499
499
|
onClick={onSubmitToOdin}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
aria-label="Retry"
|
|
500
|
+
/>
|
|
501
|
+
<IconButton
|
|
502
|
+
variant="ghost"
|
|
503
|
+
size="sm"
|
|
504
|
+
icon={<RotateCw size={12} aria-hidden />}
|
|
505
|
+
aria-label="Retry task"
|
|
507
506
|
title="Retry"
|
|
508
507
|
onClick={onRetry}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
aria-label="Delete"
|
|
508
|
+
/>
|
|
509
|
+
<IconButton
|
|
510
|
+
variant="danger"
|
|
511
|
+
size="sm"
|
|
512
|
+
icon={<Trash2 size={12} aria-hidden />}
|
|
513
|
+
aria-label="Delete task"
|
|
516
514
|
title="Delete"
|
|
517
515
|
onClick={onDelete}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
</button>
|
|
521
|
-
<button
|
|
522
|
-
type="button"
|
|
523
|
-
className="icon-btn"
|
|
524
|
-
aria-label="Move right"
|
|
525
|
-
title="Move right"
|
|
526
|
-
onClick={() => onMove(1)}
|
|
527
|
-
>
|
|
528
|
-
<ChevronRight size={14} />
|
|
529
|
-
</button>
|
|
530
|
-
</div>
|
|
516
|
+
/>
|
|
517
|
+
</Inline>
|
|
531
518
|
</div>
|
|
532
519
|
</div>
|
|
533
520
|
);
|
|
@@ -560,7 +547,7 @@ function openCreateTaskModal(
|
|
|
560
547
|
titleEl?.focus();
|
|
561
548
|
return;
|
|
562
549
|
}
|
|
563
|
-
const priority = (priorityEl?.value || 'normal') as
|
|
550
|
+
const priority = (priorityEl?.value || 'normal') as PriorityKind;
|
|
564
551
|
const tags = (tagsEl?.value || '')
|
|
565
552
|
.split(',')
|
|
566
553
|
.map((s) => s.trim())
|
|
@@ -575,6 +562,11 @@ function openCreateTaskModal(
|
|
|
575
562
|
// No assignee / agent — Odin decides.
|
|
576
563
|
});
|
|
577
564
|
modal.close();
|
|
565
|
+
// Synthetic mousedown is intentional: it signals any open outside-
|
|
566
|
+
// click listeners (e.g. WorkspaceSelector, Notifications popover)
|
|
567
|
+
// that another surface has just stolen focus, so they collapse
|
|
568
|
+
// before the toast overlays the layout. The event carries no
|
|
569
|
+
// coordinates by design — those listeners only check the dispatch.
|
|
578
570
|
if (typeof window !== 'undefined') {
|
|
579
571
|
window.dispatchEvent(new MouseEvent('mousedown'));
|
|
580
572
|
}
|
|
@@ -694,7 +686,7 @@ function openEditTaskModal(
|
|
|
694
686
|
titleEl?.focus();
|
|
695
687
|
return;
|
|
696
688
|
}
|
|
697
|
-
const priority = (priorityEl?.value || 'normal') as
|
|
689
|
+
const priority = (priorityEl?.value || 'normal') as PriorityKind;
|
|
698
690
|
const tags = (tagsEl?.value || '')
|
|
699
691
|
.split(',')
|
|
700
692
|
.map((s) => s.trim())
|
|
@@ -708,6 +700,11 @@ function openEditTaskModal(
|
|
|
708
700
|
tags,
|
|
709
701
|
});
|
|
710
702
|
modal.close();
|
|
703
|
+
// Synthetic mousedown is intentional: it signals any open outside-
|
|
704
|
+
// click listeners (e.g. WorkspaceSelector, Notifications popover)
|
|
705
|
+
// that another surface has just stolen focus, so they collapse
|
|
706
|
+
// before the toast overlays the layout. The event carries no
|
|
707
|
+
// coordinates by design — those listeners only check the dispatch.
|
|
711
708
|
if (typeof window !== 'undefined') {
|
|
712
709
|
window.dispatchEvent(new MouseEvent('mousedown'));
|
|
713
710
|
}
|
|
@@ -788,4 +785,5 @@ function openEditTaskModal(
|
|
|
788
785
|
),
|
|
789
786
|
});
|
|
790
787
|
}
|
|
788
|
+
|
|
791
789
|
export const Tasks = React.memo(TasksInner);
|