@ikenga/pkg-tasks 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +17 -51
- package/dist/features/tasks/create-task-form.js +159 -0
- package/dist/features/tasks/done-view.js +161 -0
- package/dist/features/tasks/sweeper-view.js +170 -0
- package/dist/features/tasks/task-detail-pane.js +82 -11
- package/dist/features/tasks/tasks-view.js +304 -94
- package/dist/index.html +5 -1
- package/dist/lib/assignees.js +137 -0
- package/dist/lib/bridge.js +24 -1
- package/dist/lib/esm-sh.d.ts +0 -4
- package/dist/lib/queries.js +89 -4
- package/dist/lib/shared.js +1 -1
- package/dist/lib/tasks-css.js +3 -3
- package/dist/lib/ui.js +5 -0
- package/dist/tasks.css +10 -9
- package/manifest.json +4 -11
- package/package.json +2 -2
- package/LICENSE +0 -201
- package/dist/features/tasks/view-tabs.js +0 -38
- package/dist/lib/supabase.js +0 -35
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Assignee roster — single source of truth for who a task can be owned by, used
|
|
2
|
+
// by both the create form (owner field) and the detail pane's Reassign picker.
|
|
3
|
+
//
|
|
4
|
+
// Two assignee kinds map onto the `tasks` columns: `assigned_to` (the id —
|
|
5
|
+
// an email for a human, an agent id like `cfo-agent` for an agent) and
|
|
6
|
+
// `assignee_type` ('human' | 'agent'). `assigneeIsAgent` in shared.js also
|
|
7
|
+
// treats a trailing `-agent` as the agent convention, so keep agent ids
|
|
8
|
+
// suffixed `-agent`.
|
|
9
|
+
//
|
|
10
|
+
// ## Roster resolution
|
|
11
|
+
//
|
|
12
|
+
// The roster is INJECTABLE via `hostContext.royaltiSuite.tasksRoster` at
|
|
13
|
+
// iframe-mount time. The shell delivers hostContext through the AppBridge
|
|
14
|
+
// `connectBridge` return value and the `onContextChange` callback (see
|
|
15
|
+
// bridge.js / app.js). The expected shape is:
|
|
16
|
+
//
|
|
17
|
+
// hostContext.royaltiSuite.tasksRoster = {
|
|
18
|
+
// humans: [{ value: string, label: string }], // email → display name
|
|
19
|
+
// agents: [{ id: string, label: string }], // agent-id → display name
|
|
20
|
+
// }
|
|
21
|
+
//
|
|
22
|
+
// When `tasksRoster` is present and well-formed (both arrays non-empty) the
|
|
23
|
+
// configured list takes full precedence over the static defaults below.
|
|
24
|
+
// When absent or malformed the static CURRENT_USER + AGENT_ROSTER fallback
|
|
25
|
+
// remains active — so the pkg works unchanged today and on every future
|
|
26
|
+
// install that hasn't run `skill-tasks setup` yet.
|
|
27
|
+
//
|
|
28
|
+
// ## Shell hook needed (WP-10 contract side)
|
|
29
|
+
//
|
|
30
|
+
// The shell must read `.atelier/skill-tasks/roster.json` from the current
|
|
31
|
+
// project dir (the directory passed as --project / CLAUDE_PROJECT_DIR) and
|
|
32
|
+
// inject it as `hostContext.royaltiSuite.tasksRoster` when building the
|
|
33
|
+
// hostContext object for this pkg's iframe (pkg-iframe-host.tsx). The JSON
|
|
34
|
+
// file shape mirrors the `tasksRoster` field above:
|
|
35
|
+
// { "humans": [{"value":"...", "label":"..."}],
|
|
36
|
+
// "agents": [{"id":"...", "label":"..."}] }
|
|
37
|
+
// Written by `skill-tasks` `setup` (WP-06 setup lifecycle); the shell need
|
|
38
|
+
// only pass it through — it must not transform or cache it.
|
|
39
|
+
|
|
40
|
+
// The logged-in human. TODO(hello@royalti.io): thread from
|
|
41
|
+
// hostContext.royaltiAuth once it carries the user email (mirrors the same TODO
|
|
42
|
+
// in tasks-view.js — this module is now the one place that literal lives).
|
|
43
|
+
export const CURRENT_USER = 'hello@royalti.io';
|
|
44
|
+
|
|
45
|
+
/** @typedef {{ id: string, label: string }} AgentEntry */
|
|
46
|
+
|
|
47
|
+
/** @type {AgentEntry[]} */
|
|
48
|
+
export const AGENT_ROSTER = [
|
|
49
|
+
{ id: 'cfo-agent', label: 'CFO · Finance' },
|
|
50
|
+
{ id: 'cmo-agent', label: 'CMO · Marketing' },
|
|
51
|
+
{ id: 'coo-agent', label: 'COO · Operations' },
|
|
52
|
+
{ id: 'content-agent', label: 'Content' },
|
|
53
|
+
{ id: 'outbound-agent', label: 'Outbound' },
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
/** @typedef {{ value: string, label: string, type: 'human' | 'agent' }} AssigneeOption */
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @typedef {{ value: string, label: string }} HumanEntry
|
|
60
|
+
* @typedef {{ humans: HumanEntry[], agents: AgentEntry[] }} TasksRoster
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Validate and return a configured roster from `hostContext.royaltiSuite.tasksRoster`,
|
|
65
|
+
* or `null` if absent / malformed. A valid roster has both `humans` and `agents`
|
|
66
|
+
* as non-empty arrays.
|
|
67
|
+
*
|
|
68
|
+
* @param {unknown} [hostContext]
|
|
69
|
+
* @returns {TasksRoster | null}
|
|
70
|
+
*/
|
|
71
|
+
export function resolveRoster(hostContext) {
|
|
72
|
+
const raw = /** @type {any} */ (hostContext)?.royaltiSuite?.tasksRoster;
|
|
73
|
+
if (!raw) return null;
|
|
74
|
+
const { humans, agents } = raw;
|
|
75
|
+
if (
|
|
76
|
+
!Array.isArray(humans) || humans.length === 0 ||
|
|
77
|
+
!Array.isArray(agents) || agents.length === 0
|
|
78
|
+
) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
// Basic per-entry shape validation — skip malformed entries rather than reject.
|
|
82
|
+
const validHumans = humans.filter(
|
|
83
|
+
(h) => h && typeof h.value === 'string' && typeof h.label === 'string',
|
|
84
|
+
);
|
|
85
|
+
const validAgents = agents.filter(
|
|
86
|
+
(a) => a && typeof a.id === 'string' && typeof a.label === 'string',
|
|
87
|
+
);
|
|
88
|
+
if (validHumans.length === 0 || validAgents.length === 0) return null;
|
|
89
|
+
return { humans: validHumans, agents: validAgents };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Flat option list for an assignee <select>. "Me" first (human), then each
|
|
94
|
+
* configured agent. The empty-value "Unassigned" sentinel is added by the
|
|
95
|
+
* caller's <select> so this list stays purely the real assignees.
|
|
96
|
+
*
|
|
97
|
+
* Accepts an optional `hostContext` object. When it carries a valid
|
|
98
|
+
* `royaltiSuite.tasksRoster`, the configured roster wins. Without it (or when
|
|
99
|
+
* the roster is absent/malformed) the static CURRENT_USER + AGENT_ROSTER
|
|
100
|
+
* defaults are used, so existing call sites calling the no-arg form remain
|
|
101
|
+
* correct without modification.
|
|
102
|
+
*
|
|
103
|
+
* @param {unknown} [hostContext]
|
|
104
|
+
* @returns {AssigneeOption[]}
|
|
105
|
+
*/
|
|
106
|
+
export function assigneeOptions(hostContext) {
|
|
107
|
+
const roster = resolveRoster(hostContext);
|
|
108
|
+
if (roster) {
|
|
109
|
+
return [
|
|
110
|
+
...roster.humans.map((h) => ({ value: h.value, label: h.label, type: /** @type {'human'} */ ('human') })),
|
|
111
|
+
...roster.agents.map((a) => ({ value: a.id, label: a.label, type: /** @type {'agent'} */ ('agent') })),
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
// Static fallback — unchanged behaviour.
|
|
115
|
+
return [
|
|
116
|
+
{ value: CURRENT_USER, label: 'Me', type: 'human' },
|
|
117
|
+
...AGENT_ROSTER.map((a) => ({ value: a.id, label: a.label, type: /** @type {'agent'} */ ('agent') })),
|
|
118
|
+
];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Resolve a picked `assigned_to` value back to its `assignee_type`. Falls back
|
|
123
|
+
* to the `-agent` naming convention for ids not in the roster (e.g. legacy rows
|
|
124
|
+
* or a future configured agent not yet reflected here).
|
|
125
|
+
*
|
|
126
|
+
* Accepts an optional `hostContext`; threads it through to `assigneeOptions` so
|
|
127
|
+
* the configured roster (when present) is consulted first.
|
|
128
|
+
*
|
|
129
|
+
* @param {string} value
|
|
130
|
+
* @param {unknown} [hostContext]
|
|
131
|
+
* @returns {'human' | 'agent'}
|
|
132
|
+
*/
|
|
133
|
+
export function assigneeTypeFor(value, hostContext) {
|
|
134
|
+
const match = assigneeOptions(hostContext).find((o) => o.value === value);
|
|
135
|
+
if (match) return match.type;
|
|
136
|
+
return value.endsWith('-agent') ? 'agent' : 'human';
|
|
137
|
+
}
|
package/dist/lib/bridge.js
CHANGED
|
@@ -33,7 +33,8 @@ export async function connectBridge({ name, version, onContextChange }) {
|
|
|
33
33
|
|
|
34
34
|
app.onerror = (err) => console.error('[tasks] bridge error', err);
|
|
35
35
|
// Theme is NOT applied here — app.js mirrors it from the parent <html>.
|
|
36
|
-
// We still forward context so live
|
|
36
|
+
// We still forward context so live activeFeature (side-menu) updates reach
|
|
37
|
+
// the app; data flows through host.dbQuery/dbExec, not the context payload.
|
|
37
38
|
app.onhostcontextchanged = (ctx) => {
|
|
38
39
|
onContextChange?.(ctx);
|
|
39
40
|
};
|
|
@@ -129,6 +130,28 @@ export async function hostDbQuery(sql, params = []) {
|
|
|
129
130
|
return Array.isArray(sc.rows) ? sc.rows : [];
|
|
130
131
|
}
|
|
131
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Write to the local `pa.db` via the host's `host.dbExec` verb (write-path WP).
|
|
135
|
+
* INSERT/UPDATE/DELETE only — the shell rejects reads/DDL, gates on the pkg
|
|
136
|
+
* declaring `capabilities.sqlite`, and scopes the target table to the pkg's
|
|
137
|
+
* declared `permissions['sqlite.tables']`. Resolves on success; throws on a
|
|
138
|
+
* closed/failed bridge so callers can surface the error in the mutation layer.
|
|
139
|
+
*
|
|
140
|
+
* sql: string — a single INSERT/UPDATE/DELETE statement with `?` params
|
|
141
|
+
* params: SqlValue[] — positional bind values
|
|
142
|
+
*/
|
|
143
|
+
export async function hostDbExec(sql, params = []) {
|
|
144
|
+
if (!app) throw new Error('[tasks] bridge not connected — db_exec unavailable');
|
|
145
|
+
const res = await app.callServerTool({
|
|
146
|
+
name: 'host.dbExec',
|
|
147
|
+
arguments: { sql, params },
|
|
148
|
+
});
|
|
149
|
+
const sc = res?.structuredContent;
|
|
150
|
+
if (!sc || sc.ok !== true) {
|
|
151
|
+
throw new Error(sc?.error ?? res?.content?.[0]?.text ?? 'host.dbExec failed');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
132
155
|
/** Read the current hostContext snapshot. */
|
|
133
156
|
export function getContext() {
|
|
134
157
|
return app?.getHostContext() ?? null;
|
package/dist/lib/esm-sh.d.ts
CHANGED
|
@@ -26,10 +26,6 @@ declare module 'https://esm.sh/@tanstack/react-query@5?deps=react@19.0.0' {
|
|
|
26
26
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
27
27
|
export const useQueryClient: any;
|
|
28
28
|
}
|
|
29
|
-
declare module 'https://esm.sh/@supabase/supabase-js@2' {
|
|
30
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
-
export const createClient: any;
|
|
32
|
-
}
|
|
33
29
|
declare module 'https://esm.sh/@modelcontextprotocol/ext-apps@1.7.1/app-with-deps' {
|
|
34
30
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
31
|
export const App: any;
|
package/dist/lib/queries.js
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
// migration 0025_tasks_domain.sql): every selected column exists.
|
|
4
4
|
//
|
|
5
5
|
// WP-04 read-swap: reads go through the host's `host.dbQuery` verb (local
|
|
6
|
-
// pa.db) instead of an in-iframe supabase-js client.
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// pa.db) instead of an in-iframe supabase-js client. Write-path WP: the
|
|
7
|
+
// status-update WRITE goes through `host.dbExec` (see `updateTaskStatus`), so
|
|
8
|
+
// this pkg no longer depends on supabase-js at all.
|
|
9
9
|
|
|
10
|
-
import { hostDbQuery } from './bridge.js';
|
|
10
|
+
import { hostDbExec, hostDbQuery } from './bridge.js';
|
|
11
11
|
import { queryKeys } from './query-keys.js';
|
|
12
|
+
import { CURRENT_USER } from './assignees.js';
|
|
12
13
|
|
|
13
14
|
// pa.db stores former Postgres array/json columns as TEXT (the Pg→SQLite
|
|
14
15
|
// down-map, shell migration 0025). `tags` arrives as a string, not a JS array
|
|
@@ -191,3 +192,87 @@ export function blockingTaskQuery(blockingId) {
|
|
|
191
192
|
enabled: !!blockingId,
|
|
192
193
|
};
|
|
193
194
|
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Write a task's status to the local pa.db via `host.dbExec` (write-path WP).
|
|
198
|
+
* `completed_at` is set to now when moving to `completed` and cleared to NULL
|
|
199
|
+
* otherwise, so a non-completed task never carries a stale completion stamp.
|
|
200
|
+
* The host scopes this write to the pkg's declared `sqlite.tables` (`tasks`).
|
|
201
|
+
*
|
|
202
|
+
* @param {string} taskId
|
|
203
|
+
* @param {TaskStatus} status
|
|
204
|
+
* @returns {Promise<void>}
|
|
205
|
+
*/
|
|
206
|
+
export async function updateTaskStatus(taskId, status) {
|
|
207
|
+
const completedAt = status === 'completed' ? new Date().toISOString() : null;
|
|
208
|
+
await hostDbExec('UPDATE tasks SET status = ?, completed_at = ? WHERE id = ?', [
|
|
209
|
+
status,
|
|
210
|
+
completedAt,
|
|
211
|
+
taskId,
|
|
212
|
+
]);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* @typedef {Object} CreateTaskInput
|
|
217
|
+
* @property {string} title required (NOT NULL in 0025)
|
|
218
|
+
* @property {string|null} [assignedTo] email (human) or agent id; null = unassigned
|
|
219
|
+
* @property {'human'|'agent'|null} [assigneeType]
|
|
220
|
+
* @property {TaskPriority} [priority] defaults 'medium'
|
|
221
|
+
* @property {string|null} [dueDate] ISO timestamp or null
|
|
222
|
+
* @property {string|null} [description]
|
|
223
|
+
* @property {string|null} [category]
|
|
224
|
+
*/
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Insert a new task into the local pa.db via `host.dbExec` (write-path WP).
|
|
228
|
+
* Now that `host.dbExec` allows a real INSERT (the table is in the pkg's
|
|
229
|
+
* declared `sqlite.tables`), creation no longer has to round-trip through the
|
|
230
|
+
* agent. The id is a client-generated uuid; created_at/updated_at are stamped
|
|
231
|
+
* now; status defaults to 'pending'. Only `id` + `title` are NOT NULL in
|
|
232
|
+
* migration 0025 — every other column is nullable or DB-defaulted, so we write
|
|
233
|
+
* just what the form collected and let SQLite default the rest.
|
|
234
|
+
*
|
|
235
|
+
* @param {CreateTaskInput} input
|
|
236
|
+
* @returns {Promise<string>} the new task id
|
|
237
|
+
*/
|
|
238
|
+
export async function createTask(input) {
|
|
239
|
+
const id = crypto.randomUUID();
|
|
240
|
+
const now = new Date().toISOString();
|
|
241
|
+
await hostDbExec(
|
|
242
|
+
'INSERT INTO tasks (id, title, description, status, priority, assigned_to, assignee_type, category, due_date, created_at, updated_at, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
|
243
|
+
[
|
|
244
|
+
id,
|
|
245
|
+
input.title,
|
|
246
|
+
input.description ?? null,
|
|
247
|
+
'pending',
|
|
248
|
+
input.priority ?? 'medium',
|
|
249
|
+
input.assignedTo ?? null,
|
|
250
|
+
input.assigneeType ?? null,
|
|
251
|
+
input.category ?? null,
|
|
252
|
+
input.dueDate ?? null,
|
|
253
|
+
now,
|
|
254
|
+
now,
|
|
255
|
+
CURRENT_USER,
|
|
256
|
+
],
|
|
257
|
+
);
|
|
258
|
+
return id;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Reassign a task to a different human/agent via `host.dbExec`. `assigned_to`
|
|
263
|
+
* was display-only across every view until now; this is the first write of it.
|
|
264
|
+
* `updated_at` is bumped so the staleness/triage logic (which keys off it)
|
|
265
|
+
* doesn't treat a just-reassigned task as stale. Pass `assignedTo = null` to
|
|
266
|
+
* clear the owner (unassign).
|
|
267
|
+
*
|
|
268
|
+
* @param {string} taskId
|
|
269
|
+
* @param {string|null} assignedTo
|
|
270
|
+
* @param {'human'|'agent'|null} assigneeType
|
|
271
|
+
* @returns {Promise<void>}
|
|
272
|
+
*/
|
|
273
|
+
export async function reassignTask(taskId, assignedTo, assigneeType) {
|
|
274
|
+
await hostDbExec(
|
|
275
|
+
'UPDATE tasks SET assigned_to = ?, assignee_type = ?, updated_at = ? WHERE id = ?',
|
|
276
|
+
[assignedTo, assigneeType, new Date().toISOString(), taskId],
|
|
277
|
+
);
|
|
278
|
+
}
|
package/dist/lib/shared.js
CHANGED
|
@@ -67,7 +67,7 @@ export function groupTasks(tasks, showAutoclosed) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// ─── In-body view switcher (Round 16 · D-2 / D-3) ───────────────────────────
|
|
70
|
-
/** @typedef {'tasks'|'agenda'|'triage'} TaskView */
|
|
70
|
+
/** @typedef {'tasks'|'agenda'|'triage'|'sweeper'|'done'} TaskView */
|
|
71
71
|
|
|
72
72
|
// ─── Agenda / Today (D-2) ────────────────────────────────────────────────────
|
|
73
73
|
/** @typedef {'me'|'agent'|'silent'|'deadline'} AgendaLane */
|
package/dist/lib/tasks-css.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Auto-derived from tasks.css — CSS-as-string so it loads via the script path
|
|
2
|
-
// (WebKitGTK
|
|
2
|
+
// (WebKitGTK cannot load link/fetch subresources from the about:srcdoc the
|
|
3
3
|
// shell mounts — only inlined scripts work; see index.html). app.js injects
|
|
4
|
-
// this as an inline
|
|
5
|
-
export default "/* Tasks screen — Ikenga Rung 3 / Batch 3 (08-tasks.html)\n * Reuses tokens from src/lib/ikenga/tokens.css. No new tokens.\n */\n\n/* === Frame ================================================== */\n.tk-frame {\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n background: var(--bg-surface);\n overflow: hidden;\n box-shadow: var(--shadow-2);\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n.tk-frame-head {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--space-4);\n padding: var(--space-4) var(--space-5);\n border-bottom: 1px solid var(--border-soft);\n background: linear-gradient(180deg, var(--tint-bg-active, var(--bg-surface)) 0%, var(--bg-surface) 100%);\n}\n.tk-frame-title-wrap { display: flex; align-items: center; gap: var(--space-2); }\n.tk-frame-title-mark { width: 18px; height: 18px; color: var(--tint-fg-active, var(--primary)); }\n.tk-frame-title {\n font-family: var(--font-display);\n font-weight: 500;\n font-size: var(--text-h3);\n margin: 0;\n color: var(--fg);\n}\n.tk-frame-sub {\n margin-top: 2px;\n font-size: var(--text-caption);\n color: var(--fg-muted);\n}\n.tk-frame-count {\n font-family: var(--font-mono);\n font-size: 12px;\n color: var(--fg-muted);\n font-weight: 400;\n margin-left: 6px;\n}\n\n/* === Filter bar ============================================= */\n.tk-filterbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-2);\n padding: var(--space-3) var(--space-5);\n border-bottom: 1px solid var(--border-soft);\n background: var(--bg-sunken);\n}\n.tk-filterbar .input-search-wrap { position: relative; display: inline-flex; }\n.tk-filterbar .input-search-wrap svg {\n position: absolute;\n left: 8px;\n top: 50%;\n transform: translateY(-50%);\n width: 13px;\n height: 13px;\n color: var(--fg-faint);\n}\n.tk-filterbar input[type='text'] {\n width: 280px;\n height: 28px;\n padding: 0 8px 0 28px;\n background: var(--bg-base);\n border: 1px solid var(--border-soft);\n border-radius: var(--radius-sm);\n color: var(--fg);\n font-family: inherit;\n font-size: 11.5px;\n}\n.tk-filterbar select {\n height: 28px;\n font-size: 11.5px;\n padding: 0 var(--space-2);\n background: var(--bg-base);\n border: 1px solid var(--border-soft);\n border-radius: var(--radius-sm);\n color: var(--fg);\n font-family: inherit;\n}\n.tk-filterbar .label {\n font-family: var(--font-mono);\n font-size: 10.5px;\n color: var(--fg-faint);\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n.tk-filterbar .spacer { flex: 1; }\n.tk-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 28px;\n padding: 0 10px;\n background: var(--bg-base);\n border: 1px solid var(--border-soft);\n border-radius: var(--radius-sm);\n color: var(--fg-muted);\n font-family: inherit;\n font-size: 11px;\n cursor: pointer;\n}\n.tk-toggle:hover { color: var(--fg); border-color: var(--fg-faint); }\n.tk-toggle.is-on {\n color: var(--live);\n background: var(--live-soft);\n border-color: color-mix(in srgb, var(--live) 30%, var(--border));\n}\n.tk-toggle .checkbox {\n width: 11px;\n height: 11px;\n border: 1px solid currentColor;\n border-radius: 2px;\n display: inline-grid;\n place-items: center;\n flex-shrink: 0;\n}\n.tk-toggle.is-on .checkbox::after {\n content: '✓';\n font-size: 9px;\n line-height: 1;\n color: currentColor;\n}\n\n/* === Master/detail split ==================================== */\n.tk-split {\n --list-w: 360px;\n display: grid;\n grid-template-columns: minmax(280px, var(--list-w)) 4px minmax(420px, 1fr);\n /* Bind the single row to the split's own height. Without this the implicit\n * row is `auto` (content-sized), so a tall list/detail grows the row past the\n * split and gets clipped by overflow:hidden — neither pane scrolls. */\n grid-template-rows: minmax(0, 1fr);\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n.tk-list {\n overflow-y: auto;\n /* Grid items default to min-height:auto (won't shrink below content), which\n * defeats overflow:auto. Allow shrink so the list scrolls within its track. */\n min-height: 0;\n background: var(--bg-surface);\n}\n.tk-divider {\n background: var(--border-soft);\n cursor: col-resize;\n}\n.tk-divider:hover { background: var(--tint-fg-active, var(--primary)); }\n.tk-detail {\n background: var(--bg-base);\n overflow-y: auto;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n/* === Group dividers ========================================= */\n.tk-group-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px var(--space-4) 6px var(--space-3);\n background: var(--bg-sunken);\n border-bottom: 1px solid var(--border-soft);\n border-top: 1px solid var(--border-soft);\n font-family: var(--font-mono);\n font-size: 9.5px;\n color: var(--fg-faint);\n letter-spacing: 0.1em;\n text-transform: uppercase;\n position: sticky;\n top: 0;\n z-index: 4;\n cursor: pointer;\n user-select: none;\n}\n.tk-group-head:first-child { border-top: 0; }\n.tk-group-head .ct {\n color: var(--fg-muted);\n font-variant-numeric: tabular-nums;\n}\n.tk-group-head.is-overdue { color: var(--danger); }\n.tk-group-head.is-overdue .ct { color: var(--danger); }\n.tk-group-head.is-autoclosed { color: var(--live); }\n.tk-group-head.is-autoclosed .ct { color: var(--live); }\n.tk-group-head.is-collapsed { background: var(--bg-base); }\n.tk-group-head .chev {\n width: 10px;\n height: 10px;\n color: var(--fg-faint);\n transition: transform var(--motion-fast) var(--ease-calm);\n}\n.tk-group-head.is-collapsed .chev { transform: rotate(-90deg); }\n.tk-group-label {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n/* === Task row =============================================== */\n.tk-row {\n display: grid;\n grid-template-columns: 14px 1fr auto;\n gap: var(--space-3);\n padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);\n border-bottom: 1px solid var(--border-soft);\n cursor: pointer;\n position: relative;\n background: transparent;\n text-align: left;\n width: 100%;\n border-left: 0;\n border-right: 0;\n border-top: 0;\n font: inherit;\n color: inherit;\n transition: background var(--motion-fast) var(--ease-calm);\n}\n.tk-row:hover { background: var(--bg-raised); }\n.tk-row.is-on { background: var(--bg-raised); }\n.tk-row.is-on::before {\n content: '';\n position: absolute;\n left: 0;\n top: 8px;\n bottom: 8px;\n width: 2px;\n border-radius: 2px;\n background: var(--tint-fg-active, var(--primary));\n}\n.tk-row .pri-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n margin-top: 5px;\n background: var(--fg-faint);\n flex-shrink: 0;\n}\n.tk-row .pri-dot.is-critical {\n background: var(--danger);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 20%, transparent);\n}\n.tk-row .pri-dot.is-high { background: var(--achievement); }\n.tk-row .pri-dot.is-medium { background: var(--systemic); }\n.tk-row .pri-dot.is-low { background: var(--fg-faint); }\n.tk-row .body {\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.tk-row .title {\n font-size: var(--text-body-sm);\n color: var(--fg);\n font-weight: 500;\n line-height: 1.35;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n.tk-row.is-completed .title {\n color: var(--fg-muted);\n text-decoration: line-through;\n text-decoration-thickness: 1px;\n}\n.tk-row .meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-family: var(--font-mono);\n font-size: 9.5px;\n color: var(--fg-faint);\n letter-spacing: 0.04em;\n}\n.tk-row .meta .cat {\n background: var(--bg-base);\n border: 1px solid var(--border-soft);\n color: var(--fg-muted);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n text-transform: lowercase;\n}\n.tk-row .right {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 4px;\n flex-shrink: 0;\n}\n.tk-row .due {\n font-family: var(--font-mono);\n font-size: 10px;\n color: var(--fg-muted);\n letter-spacing: 0.04em;\n white-space: nowrap;\n}\n.tk-row .due.is-overdue { color: var(--danger); font-weight: 500; }\n.tk-row .due.is-today { color: var(--achievement); }\n\n/* === Status badge =========================================== */\n.tk-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-family: var(--font-mono);\n font-size: 9.5px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n padding: 2px 6px;\n border-radius: var(--radius-xs);\n border: 1px solid var(--border-soft);\n color: var(--fg-muted);\n background: var(--bg-base);\n white-space: nowrap;\n}\n.tk-badge.is-pending { color: var(--fg-muted); }\n.tk-badge.is-in_progress {\n color: var(--live);\n background: var(--live-soft);\n border-color: color-mix(in srgb, var(--live) 30%, var(--border-soft));\n}\n.tk-badge.is-blocked {\n color: var(--danger);\n background: color-mix(in srgb, var(--danger) 12%, transparent);\n border-color: color-mix(in srgb, var(--danger) 30%, var(--border-soft));\n}\n.tk-badge.is-completed {\n color: var(--live);\n background: var(--live-soft);\n border-color: color-mix(in srgb, var(--live) 30%, var(--border-soft));\n}\n.tk-badge.is-cancelled { color: var(--fg-faint); }\n.tk-badge .dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: currentColor;\n}\n\n/* === Auto-close badge ======================================= */\n.tk-autoclose {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-family: var(--font-mono);\n font-size: 9px;\n letter-spacing: 0.06em;\n color: var(--live);\n background: var(--live-soft);\n border: 1px solid color-mix(in srgb, var(--live) 25%, var(--border-soft));\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n white-space: nowrap;\n}\n.tk-autoclose svg { width: 9px; height: 9px; }\n\n/* === Assignee chips ========================================= */\n.tk-assignee {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-family: var(--font-mono);\n font-size: 9.5px;\n letter-spacing: 0.04em;\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n border: 1px solid var(--border-soft);\n background: var(--bg-base);\n color: var(--fg-muted);\n white-space: nowrap;\n}\n.tk-assignee.is-agent {\n color: var(--agent);\n background: var(--agent-soft);\n border-color: color-mix(in srgb, var(--agent) 30%, var(--border-soft));\n}\n.tk-assignee .avatar {\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background: var(--bg-raised);\n color: var(--fg);\n display: inline-grid;\n place-items: center;\n font-size: 7.5px;\n font-weight: 600;\n text-transform: uppercase;\n}\n.tk-assignee.is-agent .avatar {\n background: color-mix(in srgb, var(--agent) 24%, transparent);\n color: var(--agent);\n}\n.tk-assignee .dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--agent);\n}\n\n/* === Exec mode pill ========================================= */\n.tk-execmode {\n font-family: var(--font-mono);\n font-size: 9px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--fg-faint);\n border: 1px dashed var(--border-soft);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n background: transparent;\n}\n.tk-execmode.is-autonomous {\n color: var(--agent);\n border-color: color-mix(in srgb, var(--agent) 30%, var(--border-soft));\n}\n.tk-execmode.is-approval_required {\n color: var(--achievement);\n border-color: color-mix(in srgb, var(--achievement) 35%, var(--border-soft));\n}\n.tk-execmode.is-report {\n color: var(--systemic);\n border-color: color-mix(in srgb, var(--systemic) 30%, var(--border-soft));\n}\n\n/* === Detail panel =========================================== */\n.tk-det-head {\n padding: var(--space-4) var(--space-5);\n border-bottom: 1px solid var(--border-soft);\n background: linear-gradient(180deg, var(--tint-bg-active, var(--bg-surface)) 0%, var(--bg-base) 100%);\n}\n.tk-det-topline {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-3);\n margin-bottom: 8px;\n font-family: var(--font-mono);\n font-size: 10.5px;\n color: var(--fg-faint);\n letter-spacing: 0.04em;\n}\n.tk-det-topline .id {\n background: var(--bg-base);\n border: 1px solid var(--border-soft);\n padding: 2px 6px;\n border-radius: var(--radius-xs);\n color: var(--fg);\n}\n.tk-det-actions { display: flex; gap: 4px; align-items: center; }\n.tk-det-title {\n font-family: var(--font-display);\n font-weight: 500;\n font-size: var(--text-h3);\n margin: 0;\n color: var(--fg);\n line-height: 1.25;\n}\n.tk-det-meta-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-2);\n margin-top: var(--space-3);\n font-family: var(--font-mono);\n font-size: 10.5px;\n color: var(--fg-faint);\n letter-spacing: 0.04em;\n}\n.tk-det-meta-row .sep { color: var(--fg-faint); }\n.tk-det-meta-row .pri-label { color: var(--achievement); display: inline-flex; align-items: center; gap: 4px; }\n.tk-det-meta-row .pri-label.is-critical { color: var(--danger); }\n.tk-det-meta-row .pri-label.is-high { color: var(--achievement); }\n.tk-det-meta-row .pri-label.is-medium { color: var(--systemic); }\n.tk-det-meta-row .pri-label.is-low { color: var(--fg-muted); }\n.tk-det-meta-row .pri-label .dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: currentColor;\n}\n.tk-det-meta-row .due-text { color: var(--fg-muted); }\n.tk-det-meta-row .due-text.is-overdue { color: var(--danger); }\n.tk-det-body {\n padding: var(--space-5);\n display: flex;\n flex-direction: column;\n gap: var(--space-5);\n flex: 1;\n}\n.tk-det-grid {\n display: grid;\n grid-template-columns: 110px 1fr;\n gap: 10px var(--space-4);\n font-size: var(--text-body-sm);\n}\n.tk-det-grid dt {\n font-family: var(--font-mono);\n font-size: 10px;\n color: var(--fg-faint);\n letter-spacing: 0.1em;\n text-transform: uppercase;\n align-self: center;\n}\n.tk-det-grid dd {\n margin: 0;\n color: var(--fg);\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-2);\n font-size: var(--text-body-sm);\n}\n.tk-det-grid dd code {\n font-family: var(--font-mono);\n font-size: 11.5px;\n background: var(--bg-sunken);\n border: 1px solid var(--border-soft);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n color: var(--fg);\n}\n.tk-section-label {\n font-family: var(--font-mono);\n font-size: 10px;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: var(--fg-faint);\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n margin-bottom: 6px;\n}\n.tk-section-label .ct {\n font-family: var(--font-mono);\n font-size: 9.5px;\n color: var(--fg-muted);\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.04em;\n text-transform: none;\n}\n.tk-deferred-pill {\n font-family: var(--font-mono);\n font-size: 9px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--fg-faint);\n border: 1px dashed var(--border);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n background: transparent;\n}\n\n.tk-desc {\n font-size: var(--text-body-sm);\n color: var(--fg-muted);\n line-height: 1.6;\n background: var(--bg-surface);\n border: 1px solid var(--border-soft);\n border-radius: var(--radius-md);\n padding: var(--space-3) var(--space-4);\n white-space: pre-wrap;\n}\n\n/* === Progress bar =========================================== */\n.tk-progress {\n flex: 1;\n height: 6px;\n background: var(--bg-sunken);\n border-radius: 3px;\n overflow: hidden;\n border: 1px solid var(--border-soft);\n min-width: 120px;\n}\n.tk-progress > span {\n display: block;\n height: 100%;\n background: var(--live);\n border-radius: 3px;\n transition: width var(--motion-fast) var(--ease-calm);\n}\n\n/* === Evidence card ========================================== */\n.tk-evidence {\n border: 1px solid color-mix(in srgb, var(--live) 30%, var(--border-soft));\n background: var(--live-soft);\n border-radius: var(--radius-md);\n padding: var(--space-3) var(--space-4);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.tk-evidence-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-3);\n}\n.tk-evidence .rule-chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-family: var(--font-mono);\n font-size: 9.5px;\n letter-spacing: 0.04em;\n color: var(--live);\n background: var(--bg-surface);\n border: 1px solid color-mix(in srgb, var(--live) 30%, var(--border-soft));\n padding: 2px 6px;\n border-radius: var(--radius-xs);\n}\n.tk-evidence .rule-chip svg { width: 10px; height: 10px; }\n.tk-evidence .rule-chip.is-flag {\n color: var(--achievement);\n border-color: color-mix(in srgb, var(--achievement) 30%, var(--border-soft));\n background: var(--bg-surface);\n}\n.tk-evidence .timestamp {\n font-family: var(--font-mono);\n font-size: 10px;\n color: var(--fg-muted);\n letter-spacing: 0.04em;\n}\n.tk-evidence .body {\n font-size: var(--text-body-sm);\n color: var(--fg);\n line-height: 1.55;\n}\n\n/* === Source-ref chips ======================================= */\n.tk-source-row {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n.tk-src {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font-family: var(--font-mono);\n font-size: 10.5px;\n color: var(--fg-muted);\n background: var(--bg-sunken);\n border: 1px solid var(--border-soft);\n padding: 3px 7px;\n border-radius: var(--radius-sm);\n text-decoration: none;\n letter-spacing: 0.02em;\n cursor: pointer;\n}\n.tk-src:hover { color: var(--fg); border-color: var(--fg-faint); }\n.tk-src svg { width: 11px; height: 11px; flex-shrink: 0; color: var(--fg-faint); }\n.tk-src.is-email svg { color: var(--mail-fg, var(--achievement)); }\n.tk-src.is-session svg { color: var(--agent); }\n.tk-src.is-git svg { color: var(--systemic); }\n\n/* === Subtasks =============================================== */\n.tk-subtasks { display: flex; flex-direction: column; gap: 4px; }\n.tk-sub-row {\n display: grid;\n grid-template-columns: auto 1fr auto;\n align-items: center;\n gap: var(--space-2);\n padding: 6px var(--space-3);\n background: var(--bg-surface);\n border: 1px solid var(--border-soft);\n border-radius: var(--radius-sm);\n font-size: var(--text-body-sm);\n cursor: pointer;\n text-align: left;\n width: 100%;\n font: inherit;\n color: inherit;\n transition: background var(--motion-fast) var(--ease-calm);\n}\n.tk-sub-row:hover { background: var(--bg-raised); }\n.tk-sub-row .name {\n color: var(--fg);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.tk-sub-row.is-completed .name {\n color: var(--fg-muted);\n text-decoration: line-through;\n}\n.tk-sub-row .due {\n font-family: var(--font-mono);\n font-size: 10px;\n color: var(--fg-faint);\n}\n\n/* === Activity timeline ====================================== */\n.tk-timeline {\n display: flex;\n flex-direction: column;\n gap: 0;\n border-left: 1px dashed var(--border);\n margin-left: 6px;\n padding-left: var(--space-4);\n position: relative;\n}\n.tk-tl-item {\n position: relative;\n padding: 6px 0;\n font-size: var(--text-body-sm);\n color: var(--fg-muted);\n}\n.tk-tl-item::before {\n content: '';\n position: absolute;\n left: calc(-1 * var(--space-4) - 4px);\n top: 12px;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--bg-sunken);\n border: 1px solid var(--border);\n}\n.tk-tl-item.is-mark::before {\n background: var(--tint-fg-active, var(--primary));\n border-color: var(--tint-fg-active, var(--primary));\n}\n.tk-tl-item.is-ok::before {\n background: var(--live);\n border-color: var(--live);\n}\n.tk-tl-item .when {\n font-family: var(--font-mono);\n font-size: 10px;\n color: var(--fg-faint);\n letter-spacing: 0.04em;\n margin-right: 6px;\n}\n.tk-tl-item .actor {\n font-family: var(--font-mono);\n font-size: 10px;\n color: var(--fg);\n background: var(--bg-sunken);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n border: 1px solid var(--border-soft);\n margin-right: 6px;\n}\n.tk-tl-item .actor.is-agent { color: var(--agent); }\n\n/* === Action footer ========================================== */\n.tk-action-bar {\n border-top: 1px solid var(--border-soft);\n background: var(--bg-sunken);\n padding: var(--space-3) var(--space-5);\n display: flex;\n align-items: center;\n gap: var(--space-2);\n margin-top: auto;\n}\n.tk-action-bar .spacer { flex: 1; }\n\n/* === Empty state in detail =================================== */\n.tk-empty {\n flex: 1;\n display: grid;\n place-items: center;\n color: var(--fg-faint);\n font-family: var(--font-mono);\n font-size: 11px;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n\n/* === Density variants for $taskId standalone (Section B) ===== */\n.tk-detail-pane.is-compact .tk-det-head { padding: var(--space-3) var(--space-4); }\n.tk-detail-pane.is-compact .tk-det-title { font-size: var(--text-h4); }\n.tk-detail-pane.is-compact .tk-det-body { padding: var(--space-4); gap: var(--space-4); }\n.tk-detail-pane.is-compact .tk-det-grid { grid-template-columns: 90px 1fr; }\n.tk-detail-pane.is-side .tk-det-head { padding: var(--space-3); }\n.tk-detail-pane.is-side .tk-det-title { font-size: 13.5px; line-height: 1.35; }\n.tk-detail-pane.is-side .tk-det-body { padding: var(--space-3); gap: var(--space-3); }\n.tk-detail-pane.is-side .tk-det-grid { display: none; }\n.tk-detail-pane.is-side .tk-section-label { margin-bottom: 4px; }\n\n/* === In-body view switcher (D-2 / D-3) ====================== */\n.ip-tabs {\n display: flex;\n align-items: center;\n gap: 0;\n padding: 0 var(--space-3);\n height: var(--tab-h, 38px);\n border-bottom: 1px solid var(--border-soft);\n background: var(--bg-sunken);\n}\n.ip-tab {\n height: var(--tab-h, 38px);\n padding: 0 var(--space-3);\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-family: var(--font-body);\n font-size: var(--text-body-sm);\n color: var(--fg-faint);\n border: 0;\n background: transparent;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -1px;\n}\n.ip-tab svg { width: 13px; height: 13px; opacity: 0.8; }\n.ip-tab:hover { color: var(--fg-muted); }\n.ip-tab.is-on { color: var(--fg); border-bottom-color: var(--tint-fg-active, var(--primary)); }\n.ip-tab.is-on svg { opacity: 1; color: var(--tint-fg-active, var(--primary)); }\n.ip-tab:focus-visible,\n.tk-toggle:focus-visible,\n.tk-group-head:focus-visible,\n.tk-row:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }\n.ip-tab-badge {\n font-family: var(--font-mono);\n font-size: 9.5px;\n line-height: 1;\n color: var(--achievement);\n background: var(--achievement-soft);\n border: 1px solid color-mix(in srgb, var(--achievement) 30%, var(--border-soft));\n border-radius: var(--radius-pill, 999px);\n padding: 2px 5px;\n}\n\n/* === Agenda / Today (D-2) =================================== */\n/* AgendaView root — rendered directly inside .tk-frame (which is overflow:hidden),\n * so it must be the scroll region itself: grow to fill the leftover height, allow\n * shrink (min-height:0), scroll its own overflow. Mirrors the Browse page's\n * `1fr` + overflow-y:auto leaf (shell .ccfg-list/.ccfg-detail). Without this the\n * agenda is clipped by the frame and can't scroll. */\n.ag-wrap { flex: 1; min-height: 0; overflow-y: auto; padding: var(--space-5); max-width: 920px; }\n.ag-head {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: var(--space-3);\n margin-bottom: var(--space-5);\n}\n.ag-date { font-family: var(--font-display); font-weight: 500; font-size: var(--text-h3); color: var(--fg); }\n.ag-summary { font-family: var(--font-mono); font-size: 10.5px; color: var(--fg-faint); letter-spacing: 0.04em; }\n.ag-summary b { color: var(--fg-muted); font-weight: 500; }\n.ag-filter-note { color: var(--achievement); font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.04em; }\n.ag-grid { display: grid; grid-template-columns: 54px 1fr; column-gap: var(--space-3); }\n.ag-time { font-family: var(--font-mono); font-size: 10px; color: var(--fg-faint); text-align: right; padding-top: 8px; letter-spacing: 0.04em; }\n.ag-lane { min-width: 0; padding-bottom: var(--space-3); }\n.ag-block {\n padding: 8px var(--space-3);\n border-radius: var(--radius-sm);\n border: 1px solid var(--border-soft);\n background: var(--bg-surface);\n border-left: 3px solid var(--fg-faint);\n margin-bottom: 6px;\n}\n.ag-block:last-child { margin-bottom: 0; }\n.ag-block.is-me { border-left-color: var(--primary); }\n.ag-block.is-agent { border-left-color: var(--agent); background: var(--agent-soft); }\n.ag-block.is-silent { border-left-color: var(--systemic); border-style: dashed; background: transparent; }\n.ag-block.is-deadline { border-left-color: var(--danger); background: color-mix(in srgb, var(--danger) 8%, transparent); }\n.ag-block.is-done { opacity: 0.6; }\n.ag-block .t { font-size: var(--text-body-sm); color: var(--fg); font-weight: 500; line-height: 1.4; }\n.ag-block.is-done .t { text-decoration: line-through; text-decoration-thickness: 1px; color: var(--fg-muted); }\n.ag-block .m { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 5px; }\n.ag-now { grid-column: 1 / -1; position: relative; height: 0; border-top: 2px solid var(--live); margin: 4px 0 10px; }\n.ag-now::after {\n content: 'now';\n position: absolute;\n right: 0;\n top: -8px;\n font-family: var(--font-mono);\n font-size: 8.5px;\n letter-spacing: 0.06em;\n color: var(--live);\n background: var(--bg-base);\n padding: 0 5px;\n}\n.ag-now::before { content: ''; position: absolute; left: -5px; top: -3px; width: 6px; height: 6px; border-radius: 50%; background: var(--live); }\n\n/* === Triage / Health (D-3) ================================== */\n/* TriageView root — same as .ag-wrap: own scroll region inside the overflow-hidden\n * frame (grow to fill, min-height:0, scroll). */\n.tr-wrap { flex: 1; min-height: 0; overflow-y: auto; padding: var(--space-5); max-width: 980px; }\n.tr-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-3); margin-bottom: var(--space-5); }\n.tr-stat { padding: var(--space-3) var(--space-4); border: 1px solid var(--border-soft); border-radius: var(--radius-md); background: var(--bg-surface); }\n/* Numbers stay --fg (12:1+ in every theme/mode) — the colored border carries\n the semantic. --achievement big-number measured 2.57:1 on a light surface. */\n.tr-stat .n { font-family: var(--font-display); font-weight: 500; font-size: 28px; line-height: 1; color: var(--fg); font-variant-numeric: tabular-nums; }\n.tr-stat .k { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-faint); margin-top: 8px; }\n.tr-stat .sub { font-family: var(--font-mono); font-size: 9px; color: var(--fg-faint); letter-spacing: 0.02em; margin-top: 3px; }\n.tr-stat.is-danger { border-color: color-mix(in srgb, var(--danger) 35%, var(--border-soft)); }\n.tr-stat.is-warn { border-color: color-mix(in srgb, var(--achievement) 35%, var(--border-soft)); }\n.tr-stat.is-sys { border-color: color-mix(in srgb, var(--systemic) 35%, var(--border-soft)); }\n.tr-bucket { margin-bottom: var(--space-5); }\n.tr-bucket-head {\n display: flex;\n align-items: center;\n gap: var(--space-2);\n font-family: var(--font-mono);\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--fg-faint);\n margin-bottom: var(--space-2);\n}\n.tr-bucket-head .ct { color: var(--fg-muted); font-variant-numeric: tabular-nums; }\n.tr-bucket-head::after { content: ''; flex: 1; height: 1px; background: var(--border-soft); }\n.tr-mini {\n display: grid;\n grid-template-columns: auto 1fr auto auto;\n align-items: center;\n gap: var(--space-3);\n padding: 8px var(--space-3);\n border-bottom: 1px solid var(--border-soft);\n}\n.tr-mini:last-child { border-bottom: 0; }\n.tr-mini .title { font-size: var(--text-body-sm); color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }\n.tr-mini .age { font-family: var(--font-mono); font-size: 10px; color: var(--fg-muted); letter-spacing: 0.04em; white-space: nowrap; }\n.tr-mini .age.is-bad { color: var(--danger); }\n.tr-more { font-family: var(--font-mono); font-size: 10px; color: var(--fg-faint); letter-spacing: 0.04em; padding: 6px var(--space-3); }\n.tr-health {\n display: flex;\n align-items: center;\n gap: var(--space-4);\n padding: var(--space-3) var(--space-4);\n border: 1px dashed var(--border);\n border-radius: var(--radius-md);\n background: var(--bg-sunken);\n font-size: var(--text-body-sm);\n color: var(--fg-muted);\n line-height: 1.55;\n}\n.tr-health .score { font-family: var(--font-display); font-weight: 500; font-size: var(--text-h2); color: var(--achievement); line-height: 1; flex-shrink: 0; }\n\n/* === De-Tailwinded utilities ================================== */\n/* The source app leaned on a handful of Tailwind utility classes for layout\n * chrome around the locked .tk-* / .ag-* / .tr-* visuals. With no Tailwind in\n * the no-build pkg, those are reproduced here as a tiny, scoped utility set\n * (named after their Tailwind origin so the htm transcription reads 1:1).\n * Everything load-bearing is still a .tk-*/.ag-*/.tr-* rule above. */\n\n/* Root layout shell (was: `flex h-full flex-col p-5`) */\n.tk-screen {\n display: flex;\n height: 100%;\n flex-direction: column;\n padding: var(--space-5);\n}\n\n/* Loading / error / empty inline states (was Tailwind utility soup) */\n.tk-loading {\n display: flex;\n align-items: center;\n gap: var(--space-2);\n padding: var(--space-4);\n font-size: var(--text-body-sm);\n color: var(--fg-muted);\n}\n.tk-error {\n margin: var(--space-4);\n display: flex;\n align-items: flex-start;\n gap: var(--space-2);\n border-radius: var(--radius-md);\n border: 1px solid color-mix(in srgb, var(--danger) 50%, transparent);\n background: color-mix(in srgb, var(--danger) 10%, transparent);\n padding: var(--space-3);\n font-size: var(--text-body-sm);\n color: var(--danger);\n}\n.tk-error svg { flex-shrink: 0; margin-top: 2px; }\n.tk-error .t { font-weight: 500; }\n.tk-error .d { font-size: var(--text-caption); opacity: 0.8; margin-top: 2px; }\n.tk-empty-box {\n margin: var(--space-4);\n display: flex;\n height: 128px;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-md);\n border: 1px dashed var(--border);\n font-size: var(--text-body-sm);\n color: var(--fg-muted);\n}\n\n/* Spinner (was: `animate-spin`) */\n.tk-spin { animation: tk-spin 1s linear infinite; }\n@keyframes tk-spin { to { transform: rotate(360deg); } }\n\n/* Detail-pane status select inline-styled in source; kept inline in the htm. */\n\n/* === Button (was: src/components/Button.tsx, Tailwind utilities) ============ */\n.tk-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n font-family: inherit;\n font-weight: 500;\n white-space: nowrap;\n cursor: pointer;\n transition: background var(--motion-fast, 120ms) var(--ease-calm, ease),\n color var(--motion-fast, 120ms) var(--ease-calm, ease),\n opacity var(--motion-fast, 120ms) var(--ease-calm, ease);\n}\n.tk-btn svg { flex-shrink: 0; }\n.tk-btn:disabled { opacity: 0.5; pointer-events: none; }\n/* sizes */\n.tk-btn.sz-md { height: 32px; padding: 0 12px; font-size: var(--text-body-sm); border-radius: var(--radius-md); }\n.tk-btn.sz-sm { height: 28px; padding: 0 10px; font-size: var(--text-caption); border-radius: var(--radius-md); gap: 4px; }\n/* variants */\n.tk-btn.v-default {\n background: var(--primary);\n color: var(--primary-fg);\n border: 1px solid transparent;\n}\n.tk-btn.v-default:hover:not(:disabled) { opacity: 0.9; }\n.tk-btn.v-outline {\n background: transparent;\n color: var(--fg);\n border: 1px solid var(--border);\n}\n.tk-btn.v-outline:hover:not(:disabled) { background: var(--bg-raised); }\n.tk-btn.v-ghost {\n background: transparent;\n color: var(--fg);\n border: 1px solid transparent;\n}\n.tk-btn.v-ghost:hover:not(:disabled) { background: var(--bg-raised); }\n.tk-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }\n\n/* Mutation error line in the detail pane footer. */\n.tk-mut-error {\n padding: 0 var(--space-5) var(--space-3);\n font-size: 11px;\n color: var(--danger);\n}\n";
|
|
4
|
+
// this as an inline style element (allowed by style-src unsafe-inline).
|
|
5
|
+
export default "/* Tasks screen — Ikenga Rung 3 / Batch 3 (08-tasks.html)\n * Reuses tokens from src/lib/ikenga/tokens.css. No new tokens.\n */\n\n/* === Frame ================================================== */\n.tk-frame {\n /* No card chrome inside the shell — the pkg pane IS the container, so the\n * content fills it flush (mirrors the design's `#tk-host > .frame` reset).\n * No border / radius / shadow / surrounding padding. */\n background: var(--bg-surface);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n.tk-frame-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-4);\n /* Slim single-row action bar (R-header): the sidebar carries domain + view\n * nav, so this bar holds only the active-view label + the New task action. */\n padding: var(--space-2) var(--space-5);\n border-bottom: 1px solid var(--border-soft);\n background: linear-gradient(180deg, var(--tint-bg-active, var(--bg-surface)) 0%, var(--bg-surface) 100%);\n}\n.tk-frame-title-wrap { display: flex; align-items: center; gap: var(--space-2); }\n.tk-frame-title-mark { width: 15px; height: 15px; color: var(--tint-fg-active, var(--primary)); }\n.tk-frame-title {\n font-family: var(--font-display);\n font-weight: 500;\n font-size: var(--text-body);\n margin: 0;\n color: var(--fg);\n}\n.tk-frame-sub {\n margin-top: 2px;\n font-size: var(--text-caption);\n color: var(--fg-muted);\n}\n.tk-frame-count {\n font-family: var(--font-mono);\n font-size: 12px;\n color: var(--fg-muted);\n font-weight: 400;\n margin-left: 6px;\n}\n\n/* === Filter bar ============================================= */\n.tk-filterbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-2);\n padding: var(--space-3) var(--space-5);\n border-bottom: 1px solid var(--border-soft);\n background: var(--bg-sunken);\n}\n.tk-filterbar .input-search-wrap { position: relative; display: inline-flex; }\n.tk-filterbar .input-search-wrap svg {\n position: absolute;\n left: 8px;\n top: 50%;\n transform: translateY(-50%);\n width: 13px;\n height: 13px;\n color: var(--fg-faint);\n}\n.tk-filterbar input[type='text'] {\n width: 280px;\n height: 28px;\n padding: 0 8px 0 28px;\n background: var(--bg-base);\n border: 1px solid var(--border-soft);\n border-radius: var(--radius-sm);\n color: var(--fg);\n font-family: inherit;\n font-size: 11.5px;\n}\n.tk-filterbar select {\n height: 28px;\n font-size: 11.5px;\n padding: 0 var(--space-2);\n background: var(--bg-base);\n border: 1px solid var(--border-soft);\n border-radius: var(--radius-sm);\n color: var(--fg);\n font-family: inherit;\n}\n.tk-filterbar .label {\n font-family: var(--font-mono);\n font-size: 10.5px;\n color: var(--fg-faint);\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n.tk-filterbar .spacer { flex: 1; }\n.tk-toggle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 28px;\n padding: 0 10px;\n background: var(--bg-base);\n border: 1px solid var(--border-soft);\n border-radius: var(--radius-sm);\n color: var(--fg-muted);\n font-family: inherit;\n font-size: 11px;\n cursor: pointer;\n}\n.tk-toggle:hover { color: var(--fg); border-color: var(--fg-faint); }\n.tk-toggle.is-on {\n color: var(--live);\n background: var(--live-soft);\n border-color: color-mix(in srgb, var(--live) 30%, var(--border));\n}\n.tk-toggle .checkbox {\n width: 11px;\n height: 11px;\n border: 1px solid currentColor;\n border-radius: 2px;\n display: inline-grid;\n place-items: center;\n flex-shrink: 0;\n}\n.tk-toggle.is-on .checkbox::after {\n content: '✓';\n font-size: 9px;\n line-height: 1;\n color: currentColor;\n}\n\n/* === Master/detail split ==================================== */\n.tk-split {\n --list-w: 360px;\n display: grid;\n grid-template-columns: minmax(280px, var(--list-w)) 4px minmax(420px, 1fr);\n /* Bind the single row to the split's own height. Without this the implicit\n * row is `auto` (content-sized), so a tall list/detail grows the row past the\n * split and gets clipped by overflow:hidden — neither pane scrolls. */\n grid-template-rows: minmax(0, 1fr);\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n.tk-list {\n overflow-y: auto;\n /* Grid items default to min-height:auto (won't shrink below content), which\n * defeats overflow:auto. Allow shrink so the list scrolls within its track. */\n min-height: 0;\n background: var(--bg-surface);\n}\n.tk-divider {\n background: var(--border-soft);\n cursor: col-resize;\n}\n.tk-divider:hover { background: var(--tint-fg-active, var(--primary)); }\n.tk-detail {\n background: var(--bg-base);\n overflow-y: auto;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n\n/* === Group dividers ========================================= */\n.tk-group-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px var(--space-4) 6px var(--space-3);\n background: var(--bg-sunken);\n border-bottom: 1px solid var(--border-soft);\n border-top: 1px solid var(--border-soft);\n font-family: var(--font-mono);\n font-size: 9.5px;\n color: var(--fg-faint);\n letter-spacing: 0.1em;\n text-transform: uppercase;\n position: sticky;\n top: 0;\n z-index: 4;\n cursor: pointer;\n user-select: none;\n}\n.tk-group-head:first-child { border-top: 0; }\n.tk-group-head .ct {\n color: var(--fg-muted);\n font-variant-numeric: tabular-nums;\n}\n.tk-group-head.is-overdue { color: var(--danger); }\n.tk-group-head.is-overdue .ct { color: var(--danger); }\n.tk-group-head.is-autoclosed { color: var(--live); }\n.tk-group-head.is-autoclosed .ct { color: var(--live); }\n.tk-group-head.is-collapsed { background: var(--bg-base); }\n.tk-group-head .chev {\n width: 10px;\n height: 10px;\n color: var(--fg-faint);\n transition: transform var(--motion-fast) var(--ease-calm);\n}\n.tk-group-head.is-collapsed .chev { transform: rotate(-90deg); }\n.tk-group-label {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n/* === Task row =============================================== */\n.tk-row {\n display: grid;\n grid-template-columns: 14px 1fr auto;\n gap: var(--space-3);\n padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);\n border-bottom: 1px solid var(--border-soft);\n cursor: pointer;\n position: relative;\n background: transparent;\n text-align: left;\n width: 100%;\n border-left: 0;\n border-right: 0;\n border-top: 0;\n font: inherit;\n color: inherit;\n transition: background var(--motion-fast) var(--ease-calm);\n}\n.tk-row:hover { background: var(--bg-raised); }\n.tk-row.is-on { background: var(--bg-raised); }\n.tk-row.is-on::before {\n content: '';\n position: absolute;\n left: 0;\n top: 8px;\n bottom: 8px;\n width: 2px;\n border-radius: 2px;\n background: var(--tint-fg-active, var(--primary));\n}\n.tk-row .pri-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n margin-top: 5px;\n background: var(--fg-faint);\n flex-shrink: 0;\n}\n.tk-row .pri-dot.is-critical {\n background: var(--danger);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--danger) 20%, transparent);\n}\n.tk-row .pri-dot.is-high { background: var(--achievement); }\n.tk-row .pri-dot.is-medium { background: var(--systemic); }\n.tk-row .pri-dot.is-low { background: var(--fg-faint); }\n.tk-row .body {\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.tk-row .title {\n font-size: var(--text-body-sm);\n color: var(--fg);\n font-weight: 500;\n line-height: 1.35;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n.tk-row.is-completed .title {\n color: var(--fg-muted);\n text-decoration: line-through;\n text-decoration-thickness: 1px;\n}\n.tk-row .meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n font-family: var(--font-mono);\n font-size: 9.5px;\n color: var(--fg-faint);\n letter-spacing: 0.04em;\n}\n.tk-row .meta .cat {\n background: var(--bg-base);\n border: 1px solid var(--border-soft);\n color: var(--fg-muted);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n text-transform: lowercase;\n}\n.tk-row .right {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 4px;\n flex-shrink: 0;\n}\n.tk-row .due {\n font-family: var(--font-mono);\n font-size: 10px;\n color: var(--fg-muted);\n letter-spacing: 0.04em;\n white-space: nowrap;\n}\n.tk-row .due.is-overdue { color: var(--danger); font-weight: 500; }\n.tk-row .due.is-today { color: var(--achievement); }\n\n/* === Status badge =========================================== */\n.tk-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-family: var(--font-mono);\n font-size: 9.5px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n padding: 2px 6px;\n border-radius: var(--radius-xs);\n border: 1px solid var(--border-soft);\n color: var(--fg-muted);\n background: var(--bg-base);\n white-space: nowrap;\n}\n.tk-badge.is-pending { color: var(--fg-muted); }\n.tk-badge.is-in_progress {\n color: var(--live);\n background: var(--live-soft);\n border-color: color-mix(in srgb, var(--live) 30%, var(--border-soft));\n}\n.tk-badge.is-blocked {\n color: var(--danger);\n background: color-mix(in srgb, var(--danger) 12%, transparent);\n border-color: color-mix(in srgb, var(--danger) 30%, var(--border-soft));\n}\n.tk-badge.is-completed {\n color: var(--live);\n background: var(--live-soft);\n border-color: color-mix(in srgb, var(--live) 30%, var(--border-soft));\n}\n.tk-badge.is-cancelled { color: var(--fg-faint); }\n.tk-badge .dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: currentColor;\n}\n\n/* === Auto-close badge ======================================= */\n.tk-autoclose {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-family: var(--font-mono);\n font-size: 9px;\n letter-spacing: 0.06em;\n color: var(--live);\n background: var(--live-soft);\n border: 1px solid color-mix(in srgb, var(--live) 25%, var(--border-soft));\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n white-space: nowrap;\n}\n.tk-autoclose svg { width: 9px; height: 9px; }\n\n/* === Assignee chips ========================================= */\n.tk-assignee {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-family: var(--font-mono);\n font-size: 9.5px;\n letter-spacing: 0.04em;\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n border: 1px solid var(--border-soft);\n background: var(--bg-base);\n color: var(--fg-muted);\n white-space: nowrap;\n}\n.tk-assignee.is-agent {\n color: var(--agent);\n background: var(--agent-soft);\n border-color: color-mix(in srgb, var(--agent) 30%, var(--border-soft));\n}\n.tk-assignee .avatar {\n width: 11px;\n height: 11px;\n border-radius: 50%;\n background: var(--bg-raised);\n color: var(--fg);\n display: inline-grid;\n place-items: center;\n font-size: 7.5px;\n font-weight: 600;\n text-transform: uppercase;\n}\n.tk-assignee.is-agent .avatar {\n background: color-mix(in srgb, var(--agent) 24%, transparent);\n color: var(--agent);\n}\n.tk-assignee .dot {\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--agent);\n}\n\n/* === Exec mode pill ========================================= */\n.tk-execmode {\n font-family: var(--font-mono);\n font-size: 9px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--fg-faint);\n border: 1px dashed var(--border-soft);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n background: transparent;\n}\n.tk-execmode.is-autonomous {\n color: var(--agent);\n border-color: color-mix(in srgb, var(--agent) 30%, var(--border-soft));\n}\n.tk-execmode.is-approval_required {\n color: var(--achievement);\n border-color: color-mix(in srgb, var(--achievement) 35%, var(--border-soft));\n}\n.tk-execmode.is-report {\n color: var(--systemic);\n border-color: color-mix(in srgb, var(--systemic) 30%, var(--border-soft));\n}\n\n/* === Detail panel =========================================== */\n.tk-det-head {\n padding: var(--space-4) var(--space-5);\n border-bottom: 1px solid var(--border-soft);\n background: linear-gradient(180deg, var(--tint-bg-active, var(--bg-surface)) 0%, var(--bg-base) 100%);\n}\n.tk-det-topline {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-3);\n margin-bottom: 8px;\n font-family: var(--font-mono);\n font-size: 10.5px;\n color: var(--fg-faint);\n letter-spacing: 0.04em;\n}\n.tk-det-topline .id {\n background: var(--bg-base);\n border: 1px solid var(--border-soft);\n padding: 2px 6px;\n border-radius: var(--radius-xs);\n color: var(--fg);\n}\n.tk-det-actions { display: flex; gap: 4px; align-items: center; }\n.tk-det-title {\n font-family: var(--font-display);\n font-weight: 500;\n font-size: var(--text-h3);\n margin: 0;\n color: var(--fg);\n line-height: 1.25;\n}\n.tk-det-meta-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-2);\n margin-top: var(--space-3);\n font-family: var(--font-mono);\n font-size: 10.5px;\n color: var(--fg-faint);\n letter-spacing: 0.04em;\n}\n.tk-det-meta-row .sep { color: var(--fg-faint); }\n.tk-det-meta-row .pri-label { color: var(--achievement); display: inline-flex; align-items: center; gap: 4px; }\n.tk-det-meta-row .pri-label.is-critical { color: var(--danger); }\n.tk-det-meta-row .pri-label.is-high { color: var(--achievement); }\n.tk-det-meta-row .pri-label.is-medium { color: var(--systemic); }\n.tk-det-meta-row .pri-label.is-low { color: var(--fg-muted); }\n.tk-det-meta-row .pri-label .dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: currentColor;\n}\n.tk-det-meta-row .due-text { color: var(--fg-muted); }\n.tk-det-meta-row .due-text.is-overdue { color: var(--danger); }\n.tk-det-body {\n padding: var(--space-5);\n display: flex;\n flex-direction: column;\n gap: var(--space-5);\n flex: 1;\n}\n.tk-det-grid {\n display: grid;\n grid-template-columns: 110px 1fr;\n gap: 10px var(--space-4);\n font-size: var(--text-body-sm);\n}\n.tk-det-grid dt {\n font-family: var(--font-mono);\n font-size: 10px;\n color: var(--fg-faint);\n letter-spacing: 0.1em;\n text-transform: uppercase;\n align-self: center;\n}\n.tk-det-grid dd {\n margin: 0;\n color: var(--fg);\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-2);\n font-size: var(--text-body-sm);\n}\n.tk-det-grid dd code {\n font-family: var(--font-mono);\n font-size: 11.5px;\n background: var(--bg-sunken);\n border: 1px solid var(--border-soft);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n color: var(--fg);\n}\n.tk-section-label {\n font-family: var(--font-mono);\n font-size: 10px;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: var(--fg-faint);\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n margin-bottom: 6px;\n}\n.tk-section-label .ct {\n font-family: var(--font-mono);\n font-size: 9.5px;\n color: var(--fg-muted);\n font-variant-numeric: tabular-nums;\n letter-spacing: 0.04em;\n text-transform: none;\n}\n.tk-deferred-pill {\n font-family: var(--font-mono);\n font-size: 9px;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n color: var(--fg-faint);\n border: 1px dashed var(--border);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n background: transparent;\n}\n\n.tk-desc {\n font-size: var(--text-body-sm);\n color: var(--fg-muted);\n line-height: 1.6;\n background: var(--bg-surface);\n border: 1px solid var(--border-soft);\n border-radius: var(--radius-md);\n padding: var(--space-3) var(--space-4);\n white-space: pre-wrap;\n}\n\n/* === Progress bar =========================================== */\n.tk-progress {\n flex: 1;\n height: 6px;\n background: var(--bg-sunken);\n border-radius: 3px;\n overflow: hidden;\n border: 1px solid var(--border-soft);\n min-width: 120px;\n}\n.tk-progress > span {\n display: block;\n height: 100%;\n background: var(--live);\n border-radius: 3px;\n transition: width var(--motion-fast) var(--ease-calm);\n}\n\n/* === Evidence card ========================================== */\n.tk-evidence {\n border: 1px solid color-mix(in srgb, var(--live) 30%, var(--border-soft));\n background: var(--live-soft);\n border-radius: var(--radius-md);\n padding: var(--space-3) var(--space-4);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.tk-evidence-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-3);\n}\n.tk-evidence .rule-chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n font-family: var(--font-mono);\n font-size: 9.5px;\n letter-spacing: 0.04em;\n color: var(--live);\n background: var(--bg-surface);\n border: 1px solid color-mix(in srgb, var(--live) 30%, var(--border-soft));\n padding: 2px 6px;\n border-radius: var(--radius-xs);\n}\n.tk-evidence .rule-chip svg { width: 10px; height: 10px; }\n.tk-evidence .rule-chip.is-flag {\n color: var(--achievement);\n border-color: color-mix(in srgb, var(--achievement) 30%, var(--border-soft));\n background: var(--bg-surface);\n}\n.tk-evidence .timestamp {\n font-family: var(--font-mono);\n font-size: 10px;\n color: var(--fg-muted);\n letter-spacing: 0.04em;\n}\n.tk-evidence .body {\n font-size: var(--text-body-sm);\n color: var(--fg);\n line-height: 1.55;\n}\n\n/* === Source-ref chips ======================================= */\n.tk-source-row {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n.tk-src {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n font-family: var(--font-mono);\n font-size: 10.5px;\n color: var(--fg-muted);\n background: var(--bg-sunken);\n border: 1px solid var(--border-soft);\n padding: 3px 7px;\n border-radius: var(--radius-sm);\n text-decoration: none;\n letter-spacing: 0.02em;\n cursor: pointer;\n}\n.tk-src:hover { color: var(--fg); border-color: var(--fg-faint); }\n.tk-src svg { width: 11px; height: 11px; flex-shrink: 0; color: var(--fg-faint); }\n.tk-src.is-email svg { color: var(--mail-fg, var(--achievement)); }\n.tk-src.is-session svg { color: var(--agent); }\n.tk-src.is-git svg { color: var(--systemic); }\n\n/* === Subtasks =============================================== */\n.tk-subtasks { display: flex; flex-direction: column; gap: 4px; }\n.tk-sub-row {\n display: grid;\n grid-template-columns: auto 1fr auto;\n align-items: center;\n gap: var(--space-2);\n padding: 6px var(--space-3);\n background: var(--bg-surface);\n border: 1px solid var(--border-soft);\n border-radius: var(--radius-sm);\n font-size: var(--text-body-sm);\n cursor: pointer;\n text-align: left;\n width: 100%;\n font: inherit;\n color: inherit;\n transition: background var(--motion-fast) var(--ease-calm);\n}\n.tk-sub-row:hover { background: var(--bg-raised); }\n.tk-sub-row .name {\n color: var(--fg);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.tk-sub-row.is-completed .name {\n color: var(--fg-muted);\n text-decoration: line-through;\n}\n.tk-sub-row .due {\n font-family: var(--font-mono);\n font-size: 10px;\n color: var(--fg-faint);\n}\n\n/* === Activity timeline ====================================== */\n.tk-timeline {\n display: flex;\n flex-direction: column;\n gap: 0;\n border-left: 1px dashed var(--border);\n margin-left: 6px;\n padding-left: var(--space-4);\n position: relative;\n}\n.tk-tl-item {\n position: relative;\n padding: 6px 0;\n font-size: var(--text-body-sm);\n color: var(--fg-muted);\n}\n.tk-tl-item::before {\n content: '';\n position: absolute;\n left: calc(-1 * var(--space-4) - 4px);\n top: 12px;\n width: 7px;\n height: 7px;\n border-radius: 50%;\n background: var(--bg-sunken);\n border: 1px solid var(--border);\n}\n.tk-tl-item.is-mark::before {\n background: var(--tint-fg-active, var(--primary));\n border-color: var(--tint-fg-active, var(--primary));\n}\n.tk-tl-item.is-ok::before {\n background: var(--live);\n border-color: var(--live);\n}\n.tk-tl-item .when {\n font-family: var(--font-mono);\n font-size: 10px;\n color: var(--fg-faint);\n letter-spacing: 0.04em;\n margin-right: 6px;\n}\n.tk-tl-item .actor {\n font-family: var(--font-mono);\n font-size: 10px;\n color: var(--fg);\n background: var(--bg-sunken);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n border: 1px solid var(--border-soft);\n margin-right: 6px;\n}\n.tk-tl-item .actor.is-agent { color: var(--agent); }\n\n/* === Action footer ========================================== */\n.tk-action-bar {\n border-top: 1px solid var(--border-soft);\n background: var(--bg-sunken);\n padding: var(--space-3) var(--space-5);\n display: flex;\n align-items: center;\n gap: var(--space-2);\n margin-top: auto;\n}\n.tk-action-bar .spacer { flex: 1; }\n\n/* === Empty state in detail =================================== */\n.tk-empty {\n flex: 1;\n display: grid;\n place-items: center;\n color: var(--fg-faint);\n font-family: var(--font-mono);\n font-size: 11px;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n}\n\n/* === Density variants for $taskId standalone (Section B) ===== */\n.tk-detail-pane.is-compact .tk-det-head { padding: var(--space-3) var(--space-4); }\n.tk-detail-pane.is-compact .tk-det-title { font-size: var(--text-h4); }\n.tk-detail-pane.is-compact .tk-det-body { padding: var(--space-4); gap: var(--space-4); }\n.tk-detail-pane.is-compact .tk-det-grid { grid-template-columns: 90px 1fr; }\n.tk-detail-pane.is-side .tk-det-head { padding: var(--space-3); }\n.tk-detail-pane.is-side .tk-det-title { font-size: 13.5px; line-height: 1.35; }\n.tk-detail-pane.is-side .tk-det-body { padding: var(--space-3); gap: var(--space-3); }\n.tk-detail-pane.is-side .tk-det-grid { display: none; }\n.tk-detail-pane.is-side .tk-section-label { margin-bottom: 4px; }\n\n/* === In-body view switcher (D-2 / D-3) ====================== */\n.ip-tabs {\n display: flex;\n align-items: center;\n gap: 0;\n padding: 0 var(--space-3);\n height: var(--tab-h, 38px);\n border-bottom: 1px solid var(--border-soft);\n background: var(--bg-sunken);\n}\n.ip-tab {\n height: var(--tab-h, 38px);\n padding: 0 var(--space-3);\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-family: var(--font-body);\n font-size: var(--text-body-sm);\n color: var(--fg-faint);\n border: 0;\n background: transparent;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -1px;\n}\n.ip-tab svg { width: 13px; height: 13px; opacity: 0.8; }\n.ip-tab:hover { color: var(--fg-muted); }\n.ip-tab.is-on { color: var(--fg); border-bottom-color: var(--tint-fg-active, var(--primary)); }\n.ip-tab.is-on svg { opacity: 1; color: var(--tint-fg-active, var(--primary)); }\n.ip-tab:focus-visible,\n.tk-toggle:focus-visible,\n.tk-group-head:focus-visible,\n.tk-row:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }\n.ip-tab-badge {\n font-family: var(--font-mono);\n font-size: 9.5px;\n line-height: 1;\n color: var(--achievement);\n background: var(--achievement-soft);\n border: 1px solid color-mix(in srgb, var(--achievement) 30%, var(--border-soft));\n border-radius: var(--radius-pill, 999px);\n padding: 2px 5px;\n}\n\n/* === Agenda / Today (D-2) =================================== */\n/* AgendaView root — rendered directly inside .tk-frame (which is overflow:hidden),\n * so it must be the scroll region itself: grow to fill the leftover height, allow\n * shrink (min-height:0), scroll its own overflow. Mirrors the Browse page's\n * `1fr` + overflow-y:auto leaf (shell .ccfg-list/.ccfg-detail). Without this the\n * agenda is clipped by the frame and can't scroll. */\n.ag-wrap { flex: 1; min-height: 0; overflow-y: auto; padding: var(--space-5); max-width: 920px; }\n.ag-head {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: var(--space-3);\n margin-bottom: var(--space-5);\n}\n.ag-date { font-family: var(--font-display); font-weight: 500; font-size: var(--text-h3); color: var(--fg); }\n.ag-summary { font-family: var(--font-mono); font-size: 10.5px; color: var(--fg-faint); letter-spacing: 0.04em; }\n.ag-summary b { color: var(--fg-muted); font-weight: 500; }\n.ag-filter-note { color: var(--achievement); font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.04em; }\n.ag-grid { display: grid; grid-template-columns: 54px 1fr; column-gap: var(--space-3); }\n.ag-time { font-family: var(--font-mono); font-size: 10px; color: var(--fg-faint); text-align: right; padding-top: 8px; letter-spacing: 0.04em; }\n.ag-lane { min-width: 0; padding-bottom: var(--space-3); }\n.ag-block {\n padding: 8px var(--space-3);\n border-radius: var(--radius-sm);\n border: 1px solid var(--border-soft);\n background: var(--bg-surface);\n border-left: 3px solid var(--fg-faint);\n margin-bottom: 6px;\n}\n.ag-block:last-child { margin-bottom: 0; }\n.ag-block.is-me { border-left-color: var(--primary); }\n.ag-block.is-agent { border-left-color: var(--agent); background: var(--agent-soft); }\n.ag-block.is-silent { border-left-color: var(--systemic); border-style: dashed; background: transparent; }\n.ag-block.is-deadline { border-left-color: var(--danger); background: color-mix(in srgb, var(--danger) 8%, transparent); }\n.ag-block.is-done { opacity: 0.6; }\n.ag-block .t { font-size: var(--text-body-sm); color: var(--fg); font-weight: 500; line-height: 1.4; }\n.ag-block.is-done .t { text-decoration: line-through; text-decoration-thickness: 1px; color: var(--fg-muted); }\n.ag-block .m { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 5px; }\n.ag-now { grid-column: 1 / -1; position: relative; height: 0; border-top: 2px solid var(--live); margin: 4px 0 10px; }\n.ag-now::after {\n content: 'now';\n position: absolute;\n right: 0;\n top: -8px;\n font-family: var(--font-mono);\n font-size: 8.5px;\n letter-spacing: 0.06em;\n color: var(--live);\n background: var(--bg-base);\n padding: 0 5px;\n}\n.ag-now::before { content: ''; position: absolute; left: -5px; top: -3px; width: 6px; height: 6px; border-radius: 50%; background: var(--live); }\n\n/* === Triage / Health (D-3) ================================== */\n/* TriageView root — same as .ag-wrap: own scroll region inside the overflow-hidden\n * frame (grow to fill, min-height:0, scroll). */\n.tr-wrap { flex: 1; min-height: 0; overflow-y: auto; padding: var(--space-5); max-width: 980px; }\n.tr-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-3); margin-bottom: var(--space-5); }\n.tr-stat { padding: var(--space-3) var(--space-4); border: 1px solid var(--border-soft); border-radius: var(--radius-md); background: var(--bg-surface); }\n/* Numbers stay --fg (12:1+ in every theme/mode) — the colored border carries\n the semantic. --achievement big-number measured 2.57:1 on a light surface. */\n.tr-stat .n { font-family: var(--font-display); font-weight: 500; font-size: 28px; line-height: 1; color: var(--fg); font-variant-numeric: tabular-nums; }\n.tr-stat .k { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--fg-faint); margin-top: 8px; }\n.tr-stat .sub { font-family: var(--font-mono); font-size: 9px; color: var(--fg-faint); letter-spacing: 0.02em; margin-top: 3px; }\n.tr-stat.is-danger { border-color: color-mix(in srgb, var(--danger) 35%, var(--border-soft)); }\n.tr-stat.is-warn { border-color: color-mix(in srgb, var(--achievement) 35%, var(--border-soft)); }\n.tr-stat.is-sys { border-color: color-mix(in srgb, var(--systemic) 35%, var(--border-soft)); }\n.tr-bucket { margin-bottom: var(--space-5); }\n.tr-bucket-head {\n display: flex;\n align-items: center;\n gap: var(--space-2);\n font-family: var(--font-mono);\n font-size: 10px;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--fg-faint);\n margin-bottom: var(--space-2);\n}\n.tr-bucket-head .ct { color: var(--fg-muted); font-variant-numeric: tabular-nums; }\n.tr-bucket-head::after { content: ''; flex: 1; height: 1px; background: var(--border-soft); }\n.tr-mini {\n display: grid;\n grid-template-columns: auto 1fr auto auto;\n align-items: center;\n gap: var(--space-3);\n padding: 8px var(--space-3);\n border-bottom: 1px solid var(--border-soft);\n}\n.tr-mini:last-child { border-bottom: 0; }\n.tr-mini .title { font-size: var(--text-body-sm); color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }\n.tr-mini .age { font-family: var(--font-mono); font-size: 10px; color: var(--fg-muted); letter-spacing: 0.04em; white-space: nowrap; }\n.tr-mini .age.is-bad { color: var(--danger); }\n.tr-more { font-family: var(--font-mono); font-size: 10px; color: var(--fg-faint); letter-spacing: 0.04em; padding: 6px var(--space-3); }\n.tr-health {\n display: flex;\n align-items: center;\n gap: var(--space-4);\n padding: var(--space-3) var(--space-4);\n border: 1px dashed var(--border);\n border-radius: var(--radius-md);\n background: var(--bg-sunken);\n font-size: var(--text-body-sm);\n color: var(--fg-muted);\n line-height: 1.55;\n}\n.tr-health .score { font-family: var(--font-display); font-weight: 500; font-size: var(--text-h2); color: var(--achievement); line-height: 1; flex-shrink: 0; }\n\n/* === De-Tailwinded utilities ================================== */\n/* The source app leaned on a handful of Tailwind utility classes for layout\n * chrome around the locked .tk-* / .ag-* / .tr-* visuals. With no Tailwind in\n * the no-build pkg, those are reproduced here as a tiny, scoped utility set\n * (named after their Tailwind origin so the htm transcription reads 1:1).\n * Everything load-bearing is still a .tk-, .ag-, .tr- rule above. */\n\n/* Root layout shell (was: `flex h-full flex-col p-5`) */\n.tk-screen {\n display: flex;\n height: 100%;\n flex-direction: column;\n}\n\n/* Loading / error / empty inline states (was Tailwind utility soup) */\n.tk-loading {\n display: flex;\n align-items: center;\n gap: var(--space-2);\n padding: var(--space-4);\n font-size: var(--text-body-sm);\n color: var(--fg-muted);\n}\n.tk-error {\n margin: var(--space-4);\n display: flex;\n align-items: flex-start;\n gap: var(--space-2);\n border-radius: var(--radius-md);\n border: 1px solid color-mix(in srgb, var(--danger) 50%, transparent);\n background: color-mix(in srgb, var(--danger) 10%, transparent);\n padding: var(--space-3);\n font-size: var(--text-body-sm);\n color: var(--danger);\n}\n.tk-error svg { flex-shrink: 0; margin-top: 2px; }\n.tk-error .t { font-weight: 500; }\n.tk-error .d { font-size: var(--text-caption); opacity: 0.8; margin-top: 2px; }\n.tk-empty-box {\n margin: var(--space-4);\n display: flex;\n height: 128px;\n align-items: center;\n justify-content: center;\n border-radius: var(--radius-md);\n border: 1px dashed var(--border);\n font-size: var(--text-body-sm);\n color: var(--fg-muted);\n}\n\n/* Spinner (was: `animate-spin`) */\n.tk-spin { animation: tk-spin 1s linear infinite; }\n@keyframes tk-spin { to { transform: rotate(360deg); } }\n\n/* Detail-pane status select inline-styled in source; kept inline in the htm. */\n\n/* === Button (was: src/components/Button.tsx, Tailwind utilities) ============ */\n.tk-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n font-family: inherit;\n font-weight: 500;\n white-space: nowrap;\n cursor: pointer;\n transition: background var(--motion-fast, 120ms) var(--ease-calm, ease),\n color var(--motion-fast, 120ms) var(--ease-calm, ease),\n opacity var(--motion-fast, 120ms) var(--ease-calm, ease);\n}\n.tk-btn svg { flex-shrink: 0; }\n.tk-btn:disabled { opacity: 0.5; pointer-events: none; }\n/* sizes */\n.tk-btn.sz-md { height: 32px; padding: 0 12px; font-size: var(--text-body-sm); border-radius: var(--radius-md); }\n.tk-btn.sz-sm { height: 28px; padding: 0 10px; font-size: var(--text-caption); border-radius: var(--radius-md); gap: 4px; }\n/* variants */\n.tk-btn.v-default {\n background: var(--primary);\n color: var(--primary-fg);\n border: 1px solid transparent;\n}\n.tk-btn.v-default:hover:not(:disabled) { opacity: 0.9; }\n.tk-btn.v-outline {\n background: transparent;\n color: var(--fg);\n border: 1px solid var(--border);\n}\n.tk-btn.v-outline:hover:not(:disabled) { background: var(--bg-raised); }\n.tk-btn.v-ghost {\n background: transparent;\n color: var(--fg);\n border: 1px solid transparent;\n}\n.tk-btn.v-ghost:hover:not(:disabled) { background: var(--bg-raised); }\n.tk-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }\n\n/* Mutation error line in the detail pane footer. */\n.tk-mut-error {\n padding: 0 var(--space-5) var(--space-3);\n font-size: 11px;\n color: var(--danger);\n}\n";
|
package/dist/lib/ui.js
CHANGED
|
@@ -57,6 +57,11 @@ const ICONS = {
|
|
|
57
57
|
mail: 'M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2zM22 6l-10 7L2 6',
|
|
58
58
|
terminal: 'M4 17l6-6-6-6M12 19h8',
|
|
59
59
|
'git-branch': 'M6 3v12M18 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM6 21a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM18 9a9 9 0 0 1-9 9',
|
|
60
|
+
// ViewTabs glyphs for Sweeper + Done. `broom` mirrors the design's SWEEP
|
|
61
|
+
// glyph (a broom strokes + handle); `check-check` is the lucide name for
|
|
62
|
+
// the "double check" used for completed items.
|
|
63
|
+
broom: 'M9.8 12.2 5 17l3 3 4.8-4.8M14 8l-3.5 3.5 4 4L18 12l-4-4z M14 8l5-5M19 3l2 2',
|
|
64
|
+
'check-check': 'M18 6 7 17l-5-5M22 10l-7.5 7.5L13 16',
|
|
60
65
|
};
|
|
61
66
|
|
|
62
67
|
// Class-name joiner — replaces clsx + tailwind-merge. With Tailwind gone there
|
package/dist/tasks.css
CHANGED
|
@@ -4,30 +4,32 @@
|
|
|
4
4
|
|
|
5
5
|
/* === Frame ================================================== */
|
|
6
6
|
.tk-frame {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
/* No card chrome inside the shell — the pkg pane IS the container, so the
|
|
8
|
+
* content fills it flush (mirrors the design's `#tk-host > .frame` reset).
|
|
9
|
+
* No border / radius / shadow / surrounding padding. */
|
|
9
10
|
background: var(--bg-surface);
|
|
10
11
|
overflow: hidden;
|
|
11
|
-
box-shadow: var(--shadow-2);
|
|
12
12
|
display: flex;
|
|
13
13
|
flex-direction: column;
|
|
14
14
|
min-height: 0;
|
|
15
15
|
}
|
|
16
16
|
.tk-frame-head {
|
|
17
17
|
display: flex;
|
|
18
|
-
align-items:
|
|
18
|
+
align-items: center;
|
|
19
19
|
justify-content: space-between;
|
|
20
20
|
gap: var(--space-4);
|
|
21
|
-
|
|
21
|
+
/* Slim single-row action bar (R-header): the sidebar carries domain + view
|
|
22
|
+
* nav, so this bar holds only the active-view label + the New task action. */
|
|
23
|
+
padding: var(--space-2) var(--space-5);
|
|
22
24
|
border-bottom: 1px solid var(--border-soft);
|
|
23
25
|
background: linear-gradient(180deg, var(--tint-bg-active, var(--bg-surface)) 0%, var(--bg-surface) 100%);
|
|
24
26
|
}
|
|
25
27
|
.tk-frame-title-wrap { display: flex; align-items: center; gap: var(--space-2); }
|
|
26
|
-
.tk-frame-title-mark { width:
|
|
28
|
+
.tk-frame-title-mark { width: 15px; height: 15px; color: var(--tint-fg-active, var(--primary)); }
|
|
27
29
|
.tk-frame-title {
|
|
28
30
|
font-family: var(--font-display);
|
|
29
31
|
font-weight: 500;
|
|
30
|
-
font-size: var(--text-
|
|
32
|
+
font-size: var(--text-body);
|
|
31
33
|
margin: 0;
|
|
32
34
|
color: var(--fg);
|
|
33
35
|
}
|
|
@@ -946,14 +948,13 @@
|
|
|
946
948
|
* chrome around the locked .tk-* / .ag-* / .tr-* visuals. With no Tailwind in
|
|
947
949
|
* the no-build pkg, those are reproduced here as a tiny, scoped utility set
|
|
948
950
|
* (named after their Tailwind origin so the htm transcription reads 1:1).
|
|
949
|
-
* Everything load-bearing is still a .tk
|
|
951
|
+
* Everything load-bearing is still a .tk-, .ag-, .tr- rule above. */
|
|
950
952
|
|
|
951
953
|
/* Root layout shell (was: `flex h-full flex-col p-5`) */
|
|
952
954
|
.tk-screen {
|
|
953
955
|
display: flex;
|
|
954
956
|
height: 100%;
|
|
955
957
|
flex-direction: column;
|
|
956
|
-
padding: var(--space-5);
|
|
957
958
|
}
|
|
958
959
|
|
|
959
960
|
/* Loading / error / empty inline states (was Tailwind utility soup) */
|
package/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "com.ikenga.tasks",
|
|
3
3
|
"name": "Tasks",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"ikenga_api": "1",
|
|
6
6
|
"kind": "embedded",
|
|
7
7
|
"author": { "name": "Royalti", "key": "royalti" },
|
|
@@ -27,27 +27,20 @@
|
|
|
27
27
|
"script-src": ["'self'", "'unsafe-inline'", "https://esm.sh"],
|
|
28
28
|
"style-src": ["'self'", "'unsafe-inline'", "https://esm.sh"],
|
|
29
29
|
"font-src": ["'self'", "https://esm.sh", "data:"],
|
|
30
|
-
"connect-src": [
|
|
31
|
-
"'self'",
|
|
32
|
-
"https://esm.sh",
|
|
33
|
-
"https://*.supabase.co",
|
|
34
|
-
"wss://*.supabase.co"
|
|
35
|
-
]
|
|
30
|
+
"connect-src": ["'self'", "https://esm.sh"]
|
|
36
31
|
}
|
|
37
32
|
},
|
|
38
33
|
|
|
39
34
|
"capabilities": {
|
|
40
|
-
"sqlite": { "db": "ikenga.local" }
|
|
41
|
-
"supabase": { "required": true }
|
|
35
|
+
"sqlite": { "db": "ikenga.local" }
|
|
42
36
|
},
|
|
43
37
|
|
|
44
38
|
"permissions": {
|
|
45
39
|
"shell.execute": [],
|
|
46
40
|
"fs.read": [],
|
|
47
41
|
"fs.write": [],
|
|
48
|
-
"net": ["https://esm.sh"
|
|
42
|
+
"net": ["https://esm.sh"],
|
|
49
43
|
"sqlite.tables": ["tasks"],
|
|
50
|
-
"supabase.tables": ["tasks", "task_comments"],
|
|
51
44
|
"vault.keys": []
|
|
52
45
|
}
|
|
53
46
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ikenga/pkg-tasks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Tasks — List, Agenda, Triage over the production tasks schema. Multi-file iframe pkg, no build step.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
"build": "echo 'no build — multi-file ESM pkg'",
|
|
22
22
|
"typecheck": "tsc -p tsconfig.dev.json"
|
|
23
23
|
}
|
|
24
|
-
}
|
|
24
|
+
}
|