From d857cf318dc04bd35475273f4d4c7c202bc184ff Mon Sep 17 00:00:00 2001 From: Gagancreates Date: Mon, 4 May 2026 22:12:05 +0530 Subject: [PATCH] feat: make track block icons configurable via yaml --- .../renderer/src/extensions/track-block.tsx | 28 +++++++++++++------ .../core/src/knowledge/ensure_daily_note.ts | 5 ++++ apps/x/packages/shared/src/track-block.ts | 1 + 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/apps/x/apps/renderer/src/extensions/track-block.tsx b/apps/x/apps/renderer/src/extensions/track-block.tsx index 536356e8..4f2a1f0a 100644 --- a/apps/x/apps/renderer/src/extensions/track-block.tsx +++ b/apps/x/apps/renderer/src/extensions/track-block.tsx @@ -1,18 +1,28 @@ import { z } from 'zod' -import { useMemo } from 'react' +import { useMemo, type ComponentType } from 'react' import { mergeAttributes, Node } from '@tiptap/react' import { ReactNodeViewRenderer, NodeViewWrapper } from '@tiptap/react' -import { Radio, Loader2, CalendarDays, Mail, Clock, ListTodo, History } from 'lucide-react' +import { Radio, Loader2, type LucideProps } from 'lucide-react' +import * as LucideIcons from 'lucide-react' import { parse as parseYaml } from 'yaml' import { TrackBlockSchema } from '@x/shared/dist/track-block.js' import { useTrackStatus } from '@/hooks/use-track-status' -function TrackIcon({ trackId, size }: { trackId: string; size: number }) { - if (trackId === 'up-next') return - if (trackId === 'calendar') return - if (trackId === 'emails') return - if (trackId === 'what-you-missed') return - if (trackId === 'priorities') return +function resolveIcon(iconName: string): ComponentType | null { + const key = iconName + .split('-') + .map(w => w.charAt(0).toUpperCase() + w.slice(1)) + .join('') + const component = (LucideIcons as Record)[key] + if (component != null) return component as ComponentType + return null +} + +function TrackIcon({ icon, size }: { icon?: string; size: number }) { + if (icon) { + const Icon = resolveIcon(icon) + if (Icon) return + } return } @@ -109,7 +119,7 @@ function TrackBlockView({ node, deleteNode, extension }: { {isRunning ? - : } + : } {trackId || 'track'} {instruction && ·} diff --git a/apps/x/packages/core/src/knowledge/ensure_daily_note.ts b/apps/x/packages/core/src/knowledge/ensure_daily_note.ts index 00f003f9..a4da6977 100644 --- a/apps/x/packages/core/src/knowledge/ensure_daily_note.ts +++ b/apps/x/packages/core/src/knowledge/ensure_daily_note.ts @@ -18,6 +18,7 @@ const SECTIONS: Section[] = [ heading: '## Up Next', track: { trackId: 'up-next', + icon: 'clock', instruction: `Write 1-3 sentences of plain markdown giving the user a shoulder-tap about what's next on their calendar today. @@ -44,6 +45,7 @@ Plain markdown prose only — no calendar block, no email block, no headings.`, heading: '## Calendar', track: { trackId: 'calendar', + icon: 'calendar-days', instruction: `Emit today's meetings as a calendar block titled "Today's Meetings". @@ -63,6 +65,7 @@ After the block, you MAY add one short markdown line per event giving useful pre heading: '## Emails', track: { trackId: 'emails', + icon: 'mail', instruction: `Maintain a digest of email threads worth the user's attention today, rendered as zero or more email blocks (one per thread). @@ -84,6 +87,7 @@ Do NOT re-list threads the user has already seen unless their state changed (new heading: '## What You Missed', track: { trackId: 'what-you-missed', + icon: 'history', instruction: `Short markdown summary of what happened yesterday that matters this morning. @@ -109,6 +113,7 @@ Do NOT manufacture content to fill the section.`, heading: '## Today\'s Priorities', track: { trackId: 'priorities', + icon: 'list-todo', instruction: `Ranked markdown list of the real, actionable items the user should focus on today. diff --git a/apps/x/packages/shared/src/track-block.ts b/apps/x/packages/shared/src/track-block.ts index 6d9ce3af..cbb370cc 100644 --- a/apps/x/packages/shared/src/track-block.ts +++ b/apps/x/packages/shared/src/track-block.ts @@ -27,6 +27,7 @@ export const TrackBlockSchema = z.object({ schedule: TrackScheduleSchema.optional(), model: z.string().optional().describe('ADVANCED — leave unset. Per-track LLM model override (e.g. "anthropic/claude-sonnet-4.6"). Only set when the user explicitly asked for a specific model for THIS track. The global default already picks a tuned model for tracks; overriding usually makes things worse, not better.'), provider: z.string().optional().describe('ADVANCED — leave unset. Per-track provider name override (e.g. "openai", "anthropic"). Only set when the user explicitly asked for a specific provider for THIS track. Almost always omitted; the global default flows through correctly.'), + icon: z.string().optional().describe('Lucide icon name for the chip (e.g. "clock", "calendar-days", "mail", "history", "list-todo"). Omit to use the default icon for this track.'), lastRunAt: z.string().optional().describe('Runtime-managed — never write this yourself'), lastRunId: z.string().optional().describe('Runtime-managed — never write this yourself'), lastRunSummary: z.string().optional().describe('Runtime-managed — never write this yourself'),