enlarge the activity-card run-status spinner (T-304)
At size 12 the rotating logo-mark spinner on in-progress collapser cards read as a static speck. Add a clideIconHero (24) control-geometry token and use it for ClideStatusIndicator on the collapser — the spinner now reads as motion at a glance; the success check / error cross share the size so the card doesn't jump when the run settles. Only call site is the collapser, so other status surfaces are untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,3 +15,5 @@ INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updat
|
|||||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', '06FB2ETJQP0CT6X7W3CWZ6NS9G', '2026-06-10 11:12:20', '2026-06-10 11:12:20', NULL, 'cb20764a77d2a15290ed61a186542095', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', '06FB2ETJQP0CT6X7W3CWZ6NS9G', '2026-06-10 11:12:20', '2026-06-10 11:12:20', NULL, 'cb20764a77d2a15290ed61a186542095', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', '06FB2EV29HSK6EJ5VF50R87VC4', '2026-06-10 11:12:25', '2026-06-10 11:12:25', NULL, 'dd9434426790edcaa556221f74c431ba', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', '06FB2EV29HSK6EJ5VF50R87VC4', '2026-06-10 11:12:25', '2026-06-10 11:12:25', NULL, 'dd9434426790edcaa556221f74c431ba', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||||
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', '06FB2G1WD1839Z90AQ5C0BHNV4', '2026-06-10 11:17:25', '2026-06-10 11:17:25', NULL, '5535e3d16bee2a2cdfcbeb84ea3fca99', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2ERREMEEF26KKHGNZBWW64', '06FB2G1WD1839Z90AQ5C0BHNV4', '2026-06-10 11:17:25', '2026-06-10 11:17:25', NULL, '5535e3d16bee2a2cdfcbeb84ea3fca99', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||||
|
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5TWC00GW0P3X02HZW', '06FB2TY91VHK7TPKPMZ11EG3TM', '2026-06-10 12:04:53', '2026-06-10 12:04:53', NULL, 'ed3717d8f6467c0a77236eda670efce5', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||||
|
INSERT INTO ticket_deps (blocker_record_id, blocked_record_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM5TWC00GW0P3X02HZW', '06FB2TY91VHK7TPKPMZ11EG3TM', '2026-06-10 12:04:53', '2026-06-10 12:05:01', '2026-06-10 12:05:01', '137cd047c9eaec01cac955b49fedd839', 2) ON CONFLICT(blocker_record_id, blocked_record_id) DO UPDATE SET updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_deps.updated_at OR (excluded.updated_at = ticket_deps.updated_at AND excluded.hash > ticket_deps.hash);
|
||||||
|
|||||||
@@ -3125,3 +3125,34 @@ INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, chang
|
|||||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB1Q1Y3CYJHD7W5F68VDB6T4', 'status', 'backlog', 'ready', NULL, '2026-06-10 11:33:26', '2026-06-10 11:33:26', '2026-06-10 11:33:26', NULL, 'f280d9b2793130900d5e470c4cb6c088', 2) ON CONFLICT(hash) DO NOTHING;
|
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB1Q1Y3CYJHD7W5F68VDB6T4', 'status', 'backlog', 'ready', NULL, '2026-06-10 11:33:26', '2026-06-10 11:33:26', '2026-06-10 11:33:26', NULL, 'f280d9b2793130900d5e470c4cb6c088', 2) ON CONFLICT(hash) DO NOTHING;
|
||||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KS233FGZE9H7ABWR', 'status', 'backlog', 'ready', NULL, '2026-06-10 11:33:35', '2026-06-10 11:33:35', '2026-06-10 11:33:35', NULL, 'bb110c716ec26f37ed4ece36b582d4d5', 2) ON CONFLICT(hash) DO NOTHING;
|
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KS233FGZE9H7ABWR', 'status', 'backlog', 'ready', NULL, '2026-06-10 11:33:35', '2026-06-10 11:33:35', '2026-06-10 11:33:35', NULL, 'bb110c716ec26f37ed4ece36b582d4d5', 2) ON CONFLICT(hash) DO NOTHING;
|
||||||
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2J2HWD66QAFDDRRWS5NM48', 'status', 'ready', 'done', NULL, '2026-06-10 11:36:35', '2026-06-10 11:36:35', '2026-06-10 11:36:35', NULL, '376bbab713452158f399f02429870546', 2) ON CONFLICT(hash) DO NOTHING;
|
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2J2HWD66QAFDDRRWS5NM48', 'status', 'ready', 'done', NULL, '2026-06-10 11:36:35', '2026-06-10 11:36:35', '2026-06-10 11:36:35', NULL, '376bbab713452158f399f02429870546', 2) ON CONFLICT(hash) DO NOTHING;
|
||||||
|
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KS233FGZE9H7ABWR', 'description', 'The logo-mark spinner shown on in-progress activity/holder cards in the Claude conversation is too small to read as a spinner — it reads as a static speck. Enlarge it so the running state is legible at a glance.
|
||||||
|
|
||||||
|
**Where**
|
||||||
|
- `ClideSpinner` (lib/widgets/src/clide_spinner.dart) — defaults to size 14; renders the logo SVG at width/height = size.
|
||||||
|
- `ClideStatusIndicator` (lib/widgets/src/clide_status_indicator.dart) — default size 14; maps running→ClideSpinner, success→check, error→cross at the same size.
|
||||||
|
- Call sites: holder_card.dart:117 and :199 pass `size: 12` — the small value the user is seeing.
|
||||||
|
|
||||||
|
**Direction (settle in review)**
|
||||||
|
- Bump the spinner size on the activity cards (the `size: 12` call sites, and/or the indicator default) to something clearly legible — pull a concrete value from the ui-design control-geometry tokens rather than a magic number.
|
||||||
|
- Keep the running spinner, success check, and error cross visually balanced at the new size (they share `size`), so the card doesn''t jump when the state settles.
|
||||||
|
- Check the other ClideSpinner/StatusIndicator consumers (status surfaces) so the bump doesn''t bloat unrelated spots — may warrant sizing the cards explicitly rather than changing the shared default.
|
||||||
|
|
||||||
|
**Acceptance**
|
||||||
|
- The in-progress spinner on conversation activity cards is comfortably distinguishable as a spinning indicator; success/error glyphs stay aligned at the same footprint.', 'The logo-mark spinner shown on in-progress activity/holder cards in the Claude conversation is too small to read as a spinner — it reads as a static speck. Enlarge it so the running state is legible at a glance.
|
||||||
|
|
||||||
|
**Where**
|
||||||
|
- `ClideSpinner` (lib/widgets/src/clide_spinner.dart) — defaults to size 14; renders the logo SVG at width/height = size.
|
||||||
|
- `ClideStatusIndicator` (lib/widgets/src/clide_status_indicator.dart) — default size 14; maps running→ClideSpinner, success→check, error→cross at the same size.
|
||||||
|
- Call sites: holder_card.dart:117 and :199 pass `size: 12` — the small value the user is seeing.
|
||||||
|
|
||||||
|
**Direction (settle in review)**
|
||||||
|
- Bump the spinner size on the activity cards (the `size: 12` call sites, and/or the indicator default) to something clearly legible — pull a concrete value from the ui-design control-geometry tokens rather than a magic number.
|
||||||
|
- Keep the running spinner, success check, and error cross visually balanced at the new size (they share `size`), so the card doesn''t jump when the state settles.
|
||||||
|
- Check the other ClideSpinner/StatusIndicator consumers (status surfaces) so the bump doesn''t bloat unrelated spots — may warrant sizing the cards explicitly rather than changing the shared default.
|
||||||
|
|
||||||
|
**Acceptance**
|
||||||
|
- The in-progress spinner on conversation activity cards is comfortably distinguishable as a spinning indicator; success/error glyphs stay aligned at the same footprint.
|
||||||
|
|
||||||
|
**Initial trial**
|
||||||
|
- For the first cut, double the current size: the `size: 12` activity-card call sites go to `size: 24`. Trial that footprint, then settle the final value in review against the control-geometry tokens.', NULL, '2026-06-10 11:55:15', '2026-06-10 11:55:15', '2026-06-10 11:55:15', NULL, '3be7c3ff633afbc67c91dd3f97c27e6f', 2) ON CONFLICT(hash) DO NOTHING;
|
||||||
|
INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KS233FGZE9H7ABWR', 'status', 'ready', 'done', NULL, '2026-06-10 12:05:17', '2026-06-10 12:05:17', '2026-06-10 12:05:17', NULL, 'a19ce81190caca50c2628a98835d41a3', 2) ON CONFLICT(hash) DO NOTHING;
|
||||||
|
|||||||
@@ -149,3 +149,5 @@ INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_
|
|||||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2G1WD1839Z90AQ5C0BHNV4', 'T-322', '2026-06-10 11:17:17', '2026-06-10 11:17:17', NULL, 'f33b1db4b67a0521f02c5c49fcfa8e6f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2G1WD1839Z90AQ5C0BHNV4', 'T-322', '2026-06-10 11:17:17', '2026-06-10 11:17:17', NULL, 'f33b1db4b67a0521f02c5c49fcfa8e6f', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2G2KHKT5CJYR0TK1WQGMD0', 'T-323', '2026-06-10 11:17:23', '2026-06-10 11:17:23', NULL, '0738a8da475a6d8d49132d1e4e753775', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2G2KHKT5CJYR0TK1WQGMD0', 'T-323', '2026-06-10 11:17:23', '2026-06-10 11:17:23', NULL, '0738a8da475a6d8d49132d1e4e753775', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||||
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2J2HWD66QAFDDRRWS5NM48', 'T-324', '2026-06-10 11:26:07', '2026-06-10 11:26:07', NULL, 'd8aa9ae4522d97b70331c42b8ab7a365', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2J2HWD66QAFDDRRWS5NM48', 'T-324', '2026-06-10 11:26:07', '2026-06-10 11:26:07', NULL, 'd8aa9ae4522d97b70331c42b8ab7a365', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||||
|
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2T11GCV1EV07DYD5BZENTM', 'T-325', '2026-06-10 12:00:52', '2026-06-10 12:00:52', NULL, '47b8f31d9337c5bbc6476d62c7f6ed4d', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||||
|
INSERT INTO ticket_idmap (record_id, ticket_id, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2TY91VHK7TPKPMZ11EG3TM', 'T-326', '2026-06-10 12:04:51', '2026-06-10 12:04:51', NULL, '348dee79d9d3fe0a8b260c1f9c47a289', 2) ON CONFLICT(record_id) DO UPDATE SET ticket_id=excluded.ticket_id, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > ticket_idmap.updated_at OR (excluded.updated_at = ticket_idmap.updated_at AND excluded.hash > ticket_idmap.hash);
|
||||||
|
|||||||
@@ -2194,3 +2194,67 @@ The tab strip is ClideTabBar (lib/widgets/src/clide_tab_bar.dart) — a Containe
|
|||||||
Keep it a hairline (1px) so it relieves the cramping without shifting the layout noticeably. Watch the tabBarBackground vs the surface behind it so the 1px gap shows the intended color, not a seam.
|
Keep it a hairline (1px) so it relieves the cramping without shifting the layout noticeably. Watch the tabBarBackground vs the surface behind it so the 1px gap shows the intended color, not a seam.
|
||||||
|
|
||||||
Acceptance: the tab strip has 1px of space above it; tabs no longer butt against the chrome edge; checked across the surfaces that use ClideTabBar.', 'done', 'low', NULL, NULL, NULL, '2026-06-10 11:26:07', '2026-06-10 11:36:35', NULL, '06aaa8609442446ebb98cd83cf241450', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
Acceptance: the tab strip has 1px of space above it; tabs no longer butt against the chrome edge; checked across the surfaces that use ClideTabBar.', 'done', 'low', NULL, NULL, NULL, '2026-06-10 11:26:07', '2026-06-10 11:36:35', NULL, '06aaa8609442446ebb98cd83cf241450', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||||
|
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KS233FGZE9H7ABWR', 'bug', '06FB0TNQM5TWC00GW0P3X02HZW', 'Activity-card spinner too small to make out — enlarge it', 'The logo-mark spinner shown on in-progress activity/holder cards in the Claude conversation is too small to read as a spinner — it reads as a static speck. Enlarge it so the running state is legible at a glance.
|
||||||
|
|
||||||
|
**Where**
|
||||||
|
- `ClideSpinner` (lib/widgets/src/clide_spinner.dart) — defaults to size 14; renders the logo SVG at width/height = size.
|
||||||
|
- `ClideStatusIndicator` (lib/widgets/src/clide_status_indicator.dart) — default size 14; maps running→ClideSpinner, success→check, error→cross at the same size.
|
||||||
|
- Call sites: holder_card.dart:117 and :199 pass `size: 12` — the small value the user is seeing.
|
||||||
|
|
||||||
|
**Direction (settle in review)**
|
||||||
|
- Bump the spinner size on the activity cards (the `size: 12` call sites, and/or the indicator default) to something clearly legible — pull a concrete value from the ui-design control-geometry tokens rather than a magic number.
|
||||||
|
- Keep the running spinner, success check, and error cross visually balanced at the new size (they share `size`), so the card doesn''t jump when the state settles.
|
||||||
|
- Check the other ClideSpinner/StatusIndicator consumers (status surfaces) so the bump doesn''t bloat unrelated spots — may warrant sizing the cards explicitly rather than changing the shared default.
|
||||||
|
|
||||||
|
**Acceptance**
|
||||||
|
- The in-progress spinner on conversation activity cards is comfortably distinguishable as a spinning indicator; success/error glyphs stay aligned at the same footprint.
|
||||||
|
|
||||||
|
**Initial trial**
|
||||||
|
- For the first cut, double the current size: the `size: 12` activity-card call sites go to `size: 24`. Trial that footprint, then settle the final value in review against the control-geometry tokens.', 'ready', 'medium', NULL, NULL, NULL, '2026-06-09 21:22:28', '2026-06-10 11:55:15', NULL, 'f1b11537f6c416bae19a6da5447a384e', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||||
|
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2T11GCV1EV07DYD5BZENTM', 'story', '06FB0TNQM5TWC00GW0P3X02HZW', 'Live tail sub-card inside expanded Bash activity cards', 'Expanded Bash activity cards should be able to show a live, scrolling tail of a file-backed source the command is following, so the user can watch the same output Claude is tailing — connected lazily, only while the card is expanded.
|
||||||
|
|
||||||
|
**Motivation**
|
||||||
|
Long-running / tailing Bash steps render only a final result block once the command finishes (see screenshot in T-304 thread). The user can''t follow progress. We want an embedded scrolling terminal sub-card inside the expanded activity card for visibility.
|
||||||
|
|
||||||
|
**Hard constraint (feasibility, established by investigation)**
|
||||||
|
Claude Code CLI executes every Bash tool itself; clide only ingests stream-json events (stream_json_session.dart -> parseTranscriptChunk). A tool_result arrives as ONE complete block — clide never sees in-flight tool output, and the controller''s in-place upsert path is hardcoded for `partial-<msg_id>` prose deltas, not tool results (controller.dart). We cannot tap Claude''s subprocess fd. So a fully-general "mirror the running command''s stdout" is NOT possible.
|
||||||
|
|
||||||
|
**Viable scope: independent file-tail on expand**
|
||||||
|
- On card expand, parse the Bash command string (AssistantToolUse.input["command"], transcript_reader.dart) for a file-backed source: `tail -f X`, `tail -n N X`, `cat X`, `less X`, known log paths, or a background task writing to a file.
|
||||||
|
- If found, clide opens its OWN read-only follower — a NativePty running `tail -f X` (native_pty.dart already exposes Stream<Uint8List>) or a Dart file-watcher — and renders it in an embedded scrolling terminal sub-card, reusing TerminalView.
|
||||||
|
- Lazy lifecycle: connect on expand, disconnect/dispose on collapse. No connection until expanded.
|
||||||
|
- Read-only & safe: clide observes the same file; it never re-runs or intercepts Claude''s command.
|
||||||
|
- Graceful when there is no file-backed source (e.g. `git push ... | tail -25`, a pipe inside Claude''s process): show a muted "no independent source to follow" note rather than faking output.
|
||||||
|
|
||||||
|
**Where**
|
||||||
|
- Card body: conversation_card.dart / conversation_view.dart (_toolUseCollapser, the Bash tool card). Add an optional live-tail segment below the RESULT segment.
|
||||||
|
- Key the live connection on AssistantToolUse.toolUseId.
|
||||||
|
- Embedded terminal: reuse lib/src/terminal TerminalView + lib/src/pty NativePty.
|
||||||
|
|
||||||
|
**Open questions (settle in review / may need a Q-record)**
|
||||||
|
- Command-parsing surface: which commands/forms count as "tailable"? Keep it a small, explicit allowlist to avoid mis-following.
|
||||||
|
- Security: only follow paths inside the workspace? How to handle absolute paths outside the repo.
|
||||||
|
- Lifecycle when the underlying command has already finished (file static) vs still running.
|
||||||
|
|
||||||
|
**Acceptance**
|
||||||
|
- Expanding a Bash card whose command tails a file shows a live scrolling terminal sub-card that updates as the file grows; collapsing it tears the follower down.
|
||||||
|
- A Bash card with no file-backed source shows a clear "nothing to follow" affordance, never a broken/empty terminal.
|
||||||
|
- No follower process is spawned until the card is expanded.', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-10 12:00:52', '2026-06-10 12:00:52', NULL, '96c66c13b16a312fa2ae625817269bb0', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||||
|
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB2TY91VHK7TPKPMZ11EG3TM', 'bug', NULL, 'ClideStatusIndicator: duplicate ''running'' key crashes AnimatedSwitcher on rapid status flips', NULL, 'backlog', 'medium', NULL, NULL, NULL, '2026-06-10 12:04:51', '2026-06-10 12:04:51', NULL, '6543e76a19006cfc6cd6ff85cd2c2d6e', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||||
|
INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB0TNQM4KS233FGZE9H7ABWR', 'bug', '06FB0TNQM5TWC00GW0P3X02HZW', 'Activity-card spinner too small to make out — enlarge it', 'The logo-mark spinner shown on in-progress activity/holder cards in the Claude conversation is too small to read as a spinner — it reads as a static speck. Enlarge it so the running state is legible at a glance.
|
||||||
|
|
||||||
|
**Where**
|
||||||
|
- `ClideSpinner` (lib/widgets/src/clide_spinner.dart) — defaults to size 14; renders the logo SVG at width/height = size.
|
||||||
|
- `ClideStatusIndicator` (lib/widgets/src/clide_status_indicator.dart) — default size 14; maps running→ClideSpinner, success→check, error→cross at the same size.
|
||||||
|
- Call sites: holder_card.dart:117 and :199 pass `size: 12` — the small value the user is seeing.
|
||||||
|
|
||||||
|
**Direction (settle in review)**
|
||||||
|
- Bump the spinner size on the activity cards (the `size: 12` call sites, and/or the indicator default) to something clearly legible — pull a concrete value from the ui-design control-geometry tokens rather than a magic number.
|
||||||
|
- Keep the running spinner, success check, and error cross visually balanced at the new size (they share `size`), so the card doesn''t jump when the state settles.
|
||||||
|
- Check the other ClideSpinner/StatusIndicator consumers (status surfaces) so the bump doesn''t bloat unrelated spots — may warrant sizing the cards explicitly rather than changing the shared default.
|
||||||
|
|
||||||
|
**Acceptance**
|
||||||
|
- The in-progress spinner on conversation activity cards is comfortably distinguishable as a spinning indicator; success/error glyphs stay aligned at the same footprint.
|
||||||
|
|
||||||
|
**Initial trial**
|
||||||
|
- For the first cut, double the current size: the `size: 12` activity-card call sites go to `size: 24`. Trial that footprint, then settle the final value in review against the control-geometry tokens.', 'done', 'medium', NULL, NULL, NULL, '2026-06-09 21:22:28', '2026-06-10 12:05:17', NULL, '1266a6e25abae600315c65aefd7d8f93', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **The activity-card run-status spinner is now legible.** At 12px the spinning
|
||||||
|
logo mark read as a static speck; the run-status indicator on collapsible
|
||||||
|
cards is bumped to a `clideIconHero` (24) so the running state is clear at a
|
||||||
|
glance. The check / cross share the size, so the card doesn't jump on settle.
|
||||||
|
(T-304)
|
||||||
|
|
||||||
## [2.2.0] — 2026-06-10
|
## [2.2.0] — 2026-06-10
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ self:
|
|||||||
# Auto-synced from pubspec.yaml `version:` by `make gen-build-info`
|
# Auto-synced from pubspec.yaml `version:` by `make gen-build-info`
|
||||||
# (runs implicitly on every build/run/test). Don't hand-edit; bump
|
# (runs implicitly on every build/run/test). Don't hand-edit; bump
|
||||||
# pubspec instead.
|
# pubspec instead.
|
||||||
version: "2.1.0"
|
version: "2.2.0"
|
||||||
homepage: https://github.com/postmeridiem/clide
|
homepage: https://github.com/postmeridiem/clide
|
||||||
license: MIT
|
license: MIT
|
||||||
license_file: assets/LICENSE
|
license_file: assets/LICENSE
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import 'package:clide/widgets/src/clide_status_indicator.dart';
|
|||||||
import 'package:clide/widgets/src/clide_tappable.dart';
|
import 'package:clide/widgets/src/clide_tappable.dart';
|
||||||
import 'package:clide/widgets/src/clide_text.dart';
|
import 'package:clide/widgets/src/clide_text.dart';
|
||||||
import 'package:clide/widgets/src/icons/chevron.dart';
|
import 'package:clide/widgets/src/icons/chevron.dart';
|
||||||
|
import 'package:clide/widgets/src/spacing.dart';
|
||||||
import 'package:clide/widgets/src/typography.dart';
|
import 'package:clide/widgets/src/typography.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
@@ -139,7 +140,7 @@ class _ClideCollapserCardState extends State<ClideCollapserCard> {
|
|||||||
// Status — the icon hard against the right edge.
|
// Status — the icon hard against the right edge.
|
||||||
if (widget.status != null) ...[
|
if (widget.status != null) ...[
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ClideStatusIndicator(status: widget.status!, size: 12),
|
ClideStatusIndicator(status: widget.status!, size: clideIconHero),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -85,5 +85,10 @@ const double clideIconHitTarget = 16;
|
|||||||
/// Emphatic icon (standalone affordances, primary action glyphs).
|
/// Emphatic icon (standalone affordances, primary action glyphs).
|
||||||
const double clideIconEmphatic = 18;
|
const double clideIconEmphatic = 18;
|
||||||
|
|
||||||
|
/// Hero icon — large enough to read at a glance, e.g. the activity-card run
|
||||||
|
/// status where the spinner must be legible as motion, not a static speck
|
||||||
|
/// (T-304). Success/error glyphs share it so the card doesn't jump on settle.
|
||||||
|
const double clideIconHero = 24;
|
||||||
|
|
||||||
/// Standard control height (tab, button, list row).
|
/// Standard control height (tab, button, list row).
|
||||||
const double clideControlHeight = 28;
|
const double clideControlHeight = 28;
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
Reference in New Issue
Block a user