@@ -1,6 +1,8 @@
1+// Package styles define styling and theming for the project.
12package styles
23
34import (
5+ "fmt"
46 "image/color"
57 "strings"
68
@@ -8,12 +10,10 @@ import (
810 "charm.land/bubbles/v2/help"
911 "charm.land/bubbles/v2/textarea"
1012 "charm.land/bubbles/v2/textinput"
11− tea "charm.land/bubbletea/v2"
1213 "charm.land/glamour/v2/ansi"
1314 "charm.land/lipgloss/v2"
1415 "github.com/alecthomas/chroma/v2"
1516 "github.com/charmbracelet/crush/internal/ui/diffview"
16− "github.com/charmbracelet/x/exp/charmtone"
1717)
1818
1919const (
@@ -566,938 +566,13 @@ func (s *Styles) DialogHelpStyles() help.Styles {
566566 return help.Styles(s.Dialog.Help)
567567}
568568
569−// DefaultStyles returns the default styles for the UI.
570−func DefaultStyles() Styles {
571− var (
572− primary = charmtone.Charple
573− secondary = charmtone.Dolly
574− tertiary = charmtone.Bok
575− // accent = charmtone.Zest
576−
577− // Backgrounds
578− bgBase = charmtone.Pepper
579− bgBaseLighter = charmtone.BBQ
580− bgSubtle = charmtone.Charcoal
581− bgOverlay = charmtone.Iron
582−
583− // Foregrounds
584− fgBase = charmtone.Ash
585− fgMuted = charmtone.Squid
586− fgHalfMuted = charmtone.Smoke
587− fgSubtle = charmtone.Oyster
588− // fgSelected = charmtone.Salt
589−
590− // Borders
591− border = charmtone.Charcoal
592− borderFocus = charmtone.Charple
593−
594− // Status
595− error = charmtone.Sriracha
596− warning = charmtone.Zest
597− info = charmtone.Malibu
598−
599− // Colors
600− white = charmtone.Butter
601−
602− blueLight = charmtone.Sardine
603− blue = charmtone.Malibu
604− blueDark = charmtone.Damson
605−
606− // yellow = charmtone.Mustard
607− yellow = charmtone.Mustard
608− // citron = charmtone.Citron
609−
610− greenLight = charmtone.Bok
611− green = charmtone.Julep
612− greenDark = charmtone.Guac
613− // greenLight = charmtone.Bok
614−
615− red = charmtone.Coral
616− redDark = charmtone.Sriracha
617− // redLight = charmtone.Salmon
618− // cherry = charmtone.Cherry
619− )
620−
621− normalBorder := lipgloss.NormalBorder()
622−
623− base := lipgloss.NewStyle().Foreground(fgBase)
624− muted := lipgloss.NewStyle().Foreground(fgMuted)
625− subtle := lipgloss.NewStyle().Foreground(fgSubtle)
626−
627− s := Styles{}
628−
629− s.Background = bgBase
630−
631− // Populate color fields
632− s.WorkingGradFromColor = primary
633− s.WorkingGradToColor = secondary
634− s.WorkingLabelColor = fgBase
635−
636− s.TextInput = textinput.Styles{
637− Focused: textinput.StyleState{
638− Text: base,
639− Placeholder: base.Foreground(fgSubtle),
640− Prompt: base.Foreground(tertiary),
641− Suggestion: base.Foreground(fgSubtle),
642− },
643− Blurred: textinput.StyleState{
644− Text: base.Foreground(fgMuted),
645− Placeholder: base.Foreground(fgSubtle),
646− Prompt: base.Foreground(fgMuted),
647− Suggestion: base.Foreground(fgSubtle),
648− },
649− Cursor: textinput.CursorStyle{
650− Color: secondary,
651− Shape: tea.CursorBlock,
652− Blink: true,
653− },
654− }
655−
656− s.Editor.Textarea = textarea.Styles{
657− Focused: textarea.StyleState{
658− Base: base,
659− Text: base,
660− LineNumber: base.Foreground(fgSubtle),
661− CursorLine: base,
662− CursorLineNumber: base.Foreground(fgSubtle),
663− Placeholder: base.Foreground(fgSubtle),
664− Prompt: base.Foreground(tertiary),
665− },
666− Blurred: textarea.StyleState{
667− Base: base,
668− Text: base.Foreground(fgMuted),
669− LineNumber: base.Foreground(fgMuted),
670− CursorLine: base,
671− CursorLineNumber: base.Foreground(fgMuted),
672− Placeholder: base.Foreground(fgSubtle),
673− Prompt: base.Foreground(fgMuted),
674− },
675− Cursor: textarea.CursorStyle{
676− Color: secondary,
677− Shape: tea.CursorBlock,
678− Blink: true,
679− },
680− }
681−
682− s.Markdown = ansi.StyleConfig{
683− Document: ansi.StyleBlock{
684− StylePrimitive: ansi.StylePrimitive{
685− // BlockPrefix: "\n",
686− // BlockSuffix: "\n",
687− Color: new(charmtone.Smoke.Hex()),
688− },
689− // Margin: new(uint(defaultMargin)),
690− },
691− BlockQuote: ansi.StyleBlock{
692− StylePrimitive: ansi.StylePrimitive{},
693− Indent: new(uint(1)),
694− IndentToken: new("│ "),
695− },
696− List: ansi.StyleList{
697− LevelIndent: defaultListIndent,
698− },
699− Heading: ansi.StyleBlock{
700− StylePrimitive: ansi.StylePrimitive{
701− BlockSuffix: "\n",
702− Color: new(charmtone.Malibu.Hex()),
703− Bold: new(true),
704− },
705− },
706− H1: ansi.StyleBlock{
707− StylePrimitive: ansi.StylePrimitive{
708− Prefix: " ",
709− Suffix: " ",
710− Color: new(charmtone.Zest.Hex()),
711− BackgroundColor: new(charmtone.Charple.Hex()),
712− Bold: new(true),
713− },
714− },
715− H2: ansi.StyleBlock{
716− StylePrimitive: ansi.StylePrimitive{
717− Prefix: "## ",
718− },
719− },
720− H3: ansi.StyleBlock{
721− StylePrimitive: ansi.StylePrimitive{
722− Prefix: "### ",
723− },
724− },
725− H4: ansi.StyleBlock{
726− StylePrimitive: ansi.StylePrimitive{
727− Prefix: "#### ",
728− },
729− },
730− H5: ansi.StyleBlock{
731− StylePrimitive: ansi.StylePrimitive{
732− Prefix: "##### ",
733− },
734− },
735− H6: ansi.StyleBlock{
736− StylePrimitive: ansi.StylePrimitive{
737− Prefix: "###### ",
738− Color: new(charmtone.Guac.Hex()),
739− Bold: new(false),
740− },
741− },
742− Strikethrough: ansi.StylePrimitive{
743− CrossedOut: new(true),
744− },
745− Emph: ansi.StylePrimitive{
746− Italic: new(true),
747− },
748− Strong: ansi.StylePrimitive{
749− Bold: new(true),
750− },
751− HorizontalRule: ansi.StylePrimitive{
752− Color: new(charmtone.Charcoal.Hex()),
753− Format: "\n--------\n",
754− },
755− Item: ansi.StylePrimitive{
756− BlockPrefix: "• ",
757− },
758− Enumeration: ansi.StylePrimitive{
759− BlockPrefix: ". ",
760− },
761− Task: ansi.StyleTask{
762− StylePrimitive: ansi.StylePrimitive{},
763− Ticked: "[✓] ",
764− Unticked: "[ ] ",
765− },
766− Link: ansi.StylePrimitive{
767− Color: new(charmtone.Zinc.Hex()),
768− Underline: new(true),
769− },
770− LinkText: ansi.StylePrimitive{
771− Color: new(charmtone.Guac.Hex()),
772− Bold: new(true),
773− },
774− Image: ansi.StylePrimitive{
775− Color: new(charmtone.Cheeky.Hex()),
776− Underline: new(true),
777− },
778− ImageText: ansi.StylePrimitive{
779− Color: new(charmtone.Squid.Hex()),
780− Format: "Image: {{.text}} →",
781− },
782− Code: ansi.StyleBlock{
783− StylePrimitive: ansi.StylePrimitive{
784− Prefix: " ",
785− Suffix: " ",
786− Color: new(charmtone.Coral.Hex()),
787− BackgroundColor: new(charmtone.Charcoal.Hex()),
788− },
789− },
790− CodeBlock: ansi.StyleCodeBlock{
791− StyleBlock: ansi.StyleBlock{
792− StylePrimitive: ansi.StylePrimitive{
793− Color: new(charmtone.Charcoal.Hex()),
794− },
795− Margin: new(uint(defaultMargin)),
796− },
797− Chroma: &ansi.Chroma{
798− Text: ansi.StylePrimitive{
799− Color: new(charmtone.Smoke.Hex()),
800− },
801− Error: ansi.StylePrimitive{
802− Color: new(charmtone.Butter.Hex()),
803− BackgroundColor: new(charmtone.Sriracha.Hex()),
804− },
805− Comment: ansi.StylePrimitive{
806− Color: new(charmtone.Oyster.Hex()),
807− },
808− CommentPreproc: ansi.StylePrimitive{
809− Color: new(charmtone.Bengal.Hex()),
810− },
811− Keyword: ansi.StylePrimitive{
812− Color: new(charmtone.Malibu.Hex()),
813− },
814− KeywordReserved: ansi.StylePrimitive{
815− Color: new(charmtone.Pony.Hex()),
816− },
817− KeywordNamespace: ansi.StylePrimitive{
818− Color: new(charmtone.Pony.Hex()),
819− },
820− KeywordType: ansi.StylePrimitive{
821− Color: new(charmtone.Guppy.Hex()),
822− },
823− Operator: ansi.StylePrimitive{
824− Color: new(charmtone.Salmon.Hex()),
825− },
826− Punctuation: ansi.StylePrimitive{
827− Color: new(charmtone.Zest.Hex()),
828− },
829− Name: ansi.StylePrimitive{
830− Color: new(charmtone.Smoke.Hex()),
831− },
832− NameBuiltin: ansi.StylePrimitive{
833− Color: new(charmtone.Cheeky.Hex()),
834− },
835− NameTag: ansi.StylePrimitive{
836− Color: new(charmtone.Mauve.Hex()),
837− },
838− NameAttribute: ansi.StylePrimitive{
839− Color: new(charmtone.Hazy.Hex()),
840− },
841− NameClass: ansi.StylePrimitive{
842− Color: new(charmtone.Salt.Hex()),
843− Underline: new(true),
844− Bold: new(true),
845− },
846− NameDecorator: ansi.StylePrimitive{
847− Color: new(charmtone.Citron.Hex()),
848− },
849− NameFunction: ansi.StylePrimitive{
850− Color: new(charmtone.Guac.Hex()),
851− },
852− LiteralNumber: ansi.StylePrimitive{
853− Color: new(charmtone.Julep.Hex()),
854− },
855− LiteralString: ansi.StylePrimitive{
856− Color: new(charmtone.Cumin.Hex()),
857− },
858− LiteralStringEscape: ansi.StylePrimitive{
859− Color: new(charmtone.Bok.Hex()),
860− },
861− GenericDeleted: ansi.StylePrimitive{
862− Color: new(charmtone.Coral.Hex()),
863− },
864− GenericEmph: ansi.StylePrimitive{
865− Italic: new(true),
866− },
867− GenericInserted: ansi.StylePrimitive{
868− Color: new(charmtone.Guac.Hex()),
869− },
870− GenericStrong: ansi.StylePrimitive{
871− Bold: new(true),
872− },
873− GenericSubheading: ansi.StylePrimitive{
874− Color: new(charmtone.Squid.Hex()),
875− },
876− Background: ansi.StylePrimitive{
877− BackgroundColor: new(charmtone.Charcoal.Hex()),
878− },
879− },
880− },
881− Table: ansi.StyleTable{
882− StyleBlock: ansi.StyleBlock{
883− StylePrimitive: ansi.StylePrimitive{},
884− },
885− },
886− DefinitionDescription: ansi.StylePrimitive{
887− BlockPrefix: "\n ",
888− },
889− }
890−
891− // QuietMarkdown style - muted colors on subtle background for thinking content.
892− plainBg := new(bgBaseLighter.Hex())
893− plainFg := new(fgMuted.Hex())
894− s.QuietMarkdown = ansi.StyleConfig{
895− Document: ansi.StyleBlock{
896− StylePrimitive: ansi.StylePrimitive{
897− Color: plainFg,
898− BackgroundColor: plainBg,
899− },
900− },
901− BlockQuote: ansi.StyleBlock{
902− StylePrimitive: ansi.StylePrimitive{
903− Color: plainFg,
904− BackgroundColor: plainBg,
905− },
906− Indent: new(uint(1)),
907− IndentToken: new("│ "),
908− },
909− List: ansi.StyleList{
910− LevelIndent: defaultListIndent,
911− },
912− Heading: ansi.StyleBlock{
913− StylePrimitive: ansi.StylePrimitive{
914− BlockSuffix: "\n",
915− Bold: new(true),
916− Color: plainFg,
917− BackgroundColor: plainBg,
918− },
919− },
920− H1: ansi.StyleBlock{
921− StylePrimitive: ansi.StylePrimitive{
922− Prefix: " ",
923− Suffix: " ",
924− Bold: new(true),
925− Color: plainFg,
926− BackgroundColor: plainBg,
927− },
928− },
929− H2: ansi.StyleBlock{
930− StylePrimitive: ansi.StylePrimitive{
931− Prefix: "## ",
932− Color: plainFg,
933− BackgroundColor: plainBg,
934− },
935− },
936− H3: ansi.StyleBlock{
937− StylePrimitive: ansi.StylePrimitive{
938− Prefix: "### ",
939− Color: plainFg,
940− BackgroundColor: plainBg,
941− },
942− },
943− H4: ansi.StyleBlock{
944− StylePrimitive: ansi.StylePrimitive{
945− Prefix: "#### ",
946− Color: plainFg,
947− BackgroundColor: plainBg,
948− },
949− },
950− H5: ansi.StyleBlock{
951− StylePrimitive: ansi.StylePrimitive{
952− Prefix: "##### ",
953− Color: plainFg,
954− BackgroundColor: plainBg,
955− },
956− },
957− H6: ansi.StyleBlock{
958− StylePrimitive: ansi.StylePrimitive{
959− Prefix: "###### ",
960− Color: plainFg,
961− BackgroundColor: plainBg,
962− },
963− },
964− Strikethrough: ansi.StylePrimitive{
965− CrossedOut: new(true),
966− Color: plainFg,
967− BackgroundColor: plainBg,
968− },
969− Emph: ansi.StylePrimitive{
970− Italic: new(true),
971− Color: plainFg,
972− BackgroundColor: plainBg,
973− },
974− Strong: ansi.StylePrimitive{
975− Bold: new(true),
976− Color: plainFg,
977− BackgroundColor: plainBg,
978− },
979− HorizontalRule: ansi.StylePrimitive{
980− Format: "\n--------\n",
981− Color: plainFg,
982− BackgroundColor: plainBg,
983− },
984− Item: ansi.StylePrimitive{
985− BlockPrefix: "• ",
986− Color: plainFg,
987− BackgroundColor: plainBg,
988− },
989− Enumeration: ansi.StylePrimitive{
990− BlockPrefix: ". ",
991− Color: plainFg,
992− BackgroundColor: plainBg,
993− },
994− Task: ansi.StyleTask{
995− StylePrimitive: ansi.StylePrimitive{
996− Color: plainFg,
997− BackgroundColor: plainBg,
998− },
999− Ticked: "[✓] ",
1000− Unticked: "[ ] ",
1001− },
1002− Link: ansi.StylePrimitive{
1003− Underline: new(true),
1004− Color: plainFg,
1005− BackgroundColor: plainBg,
1006− },
1007− LinkText: ansi.StylePrimitive{
1008− Bold: new(true),
1009− Color: plainFg,
1010− BackgroundColor: plainBg,
1011− },
1012− Image: ansi.StylePrimitive{
1013− Underline: new(true),
1014− Color: plainFg,
1015− BackgroundColor: plainBg,
1016− },
1017− ImageText: ansi.StylePrimitive{
1018− Format: "Image: {{.text}} →",
1019− Color: plainFg,
1020− BackgroundColor: plainBg,
1021− },
1022− Code: ansi.StyleBlock{
1023− StylePrimitive: ansi.StylePrimitive{
1024− Prefix: " ",
1025− Suffix: " ",
1026− Color: plainFg,
1027− BackgroundColor: plainBg,
1028− },
1029− },
1030− CodeBlock: ansi.StyleCodeBlock{
1031− StyleBlock: ansi.StyleBlock{
1032− StylePrimitive: ansi.StylePrimitive{
1033− Color: plainFg,
1034− BackgroundColor: plainBg,
1035− },
1036− Margin: new(uint(defaultMargin)),
1037− },
1038− },
1039− Table: ansi.StyleTable{
1040− StyleBlock: ansi.StyleBlock{
1041− StylePrimitive: ansi.StylePrimitive{
1042− Color: plainFg,
1043− BackgroundColor: plainBg,
1044− },
1045− },
1046− },
1047− DefinitionDescription: ansi.StylePrimitive{
1048− BlockPrefix: "\n ",
1049− Color: plainFg,
1050− BackgroundColor: plainBg,
1051− },
1052− }
1053−
1054− s.Help = help.Styles{
1055− ShortKey: base.Foreground(fgMuted),
1056− ShortDesc: base.Foreground(fgSubtle),
1057− ShortSeparator: base.Foreground(border),
1058− Ellipsis: base.Foreground(border),
1059− FullKey: base.Foreground(fgMuted),
1060− FullDesc: base.Foreground(fgSubtle),
1061− FullSeparator: base.Foreground(border),
1062− }
1063−
1064− s.Diff = diffview.Style{
1065− DividerLine: diffview.LineStyle{
1066− LineNumber: lipgloss.NewStyle().
1067− Foreground(fgHalfMuted).
1068− Background(bgBaseLighter),
1069− Code: lipgloss.NewStyle().
1070− Foreground(fgHalfMuted).
1071− Background(bgBaseLighter),
1072− },
1073− MissingLine: diffview.LineStyle{
1074− LineNumber: lipgloss.NewStyle().
1075− Background(bgBaseLighter),
1076− Code: lipgloss.NewStyle().
1077− Background(bgBaseLighter),
1078− },
1079− EqualLine: diffview.LineStyle{
1080− LineNumber: lipgloss.NewStyle().
1081− Foreground(fgMuted).
1082− Background(bgBase),
1083− Code: lipgloss.NewStyle().
1084− Foreground(fgMuted).
1085− Background(bgBase),
1086− },
1087− InsertLine: diffview.LineStyle{
1088− LineNumber: lipgloss.NewStyle().
1089− Foreground(lipgloss.Color("#629657")).
1090− Background(lipgloss.Color("#2b322a")),
1091− Symbol: lipgloss.NewStyle().
1092− Foreground(lipgloss.Color("#629657")).
1093− Background(lipgloss.Color("#323931")),
1094− Code: lipgloss.NewStyle().
1095− Background(lipgloss.Color("#323931")),
1096− },
1097− DeleteLine: diffview.LineStyle{
1098− LineNumber: lipgloss.NewStyle().
1099− Foreground(lipgloss.Color("#a45c59")).
1100− Background(lipgloss.Color("#312929")),
1101− Symbol: lipgloss.NewStyle().
1102− Foreground(lipgloss.Color("#a45c59")).
1103− Background(lipgloss.Color("#383030")),
1104− Code: lipgloss.NewStyle().
1105− Background(lipgloss.Color("#383030")),
1106− },
1107− Filename: diffview.LineStyle{
1108− LineNumber: lipgloss.NewStyle().
1109− Foreground(fgHalfMuted).
1110− Background(bgBaseLighter),
1111− Code: lipgloss.NewStyle().
1112− Foreground(fgHalfMuted).
1113− Background(bgBaseLighter),
1114− },
1115− }
1116−
1117− s.FilePicker = filepicker.Styles{
1118− DisabledCursor: base.Foreground(fgMuted),
1119− Cursor: base.Foreground(fgBase),
1120− Symlink: base.Foreground(fgSubtle),
1121− Directory: base.Foreground(primary),
1122− File: base.Foreground(fgBase),
1123− DisabledFile: base.Foreground(fgMuted),
1124− DisabledSelected: base.Background(bgOverlay).Foreground(fgMuted),
1125− Permission: base.Foreground(fgMuted),
1126− Selected: base.Background(primary).Foreground(fgBase),
1127− FileSize: base.Foreground(fgMuted),
1128− EmptyDirectory: base.Foreground(fgMuted).PaddingLeft(2).SetString("Empty directory"),
1129− }
1130−
1131− // borders
1132− s.ToolCallSuccess = lipgloss.NewStyle().Foreground(green).SetString(ToolSuccess)
1133−
1134− s.Header.Charm = base.Foreground(secondary)
1135− s.Header.Diagonals = base.Foreground(primary)
1136− s.Header.Percentage = muted
1137− s.Header.Keystroke = muted
1138− s.Header.KeystrokeTip = subtle
1139− s.Header.WorkingDir = muted
1140− s.Header.Separator = subtle
1141− s.Header.Wrapper = lipgloss.NewStyle().Foreground(fgBase)
1142− s.Header.LogoGradCanvas = lipgloss.NewStyle()
1143− s.Header.LogoGradFromColor = secondary
1144− s.Header.LogoGradToColor = primary
1145−
1146− s.CompactDetails.Title = base
1147− s.CompactDetails.View = base.Padding(0, 1, 1, 1).Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1148− s.CompactDetails.Version = lipgloss.NewStyle().Foreground(border)
1149−
1150− // Tool rendering styles
1151− s.Tool.IconPending = base.Foreground(greenDark).SetString(ToolPending)
1152− s.Tool.IconSuccess = base.Foreground(green).SetString(ToolSuccess)
1153− s.Tool.IconError = base.Foreground(redDark).SetString(ToolError)
1154− s.Tool.IconCancelled = muted.SetString(ToolPending)
1155−
1156− s.Tool.NameNormal = base.Foreground(blue)
1157− s.Tool.NameNested = base.Foreground(blue)
1158−
1159− s.Tool.ParamMain = subtle
1160− s.Tool.ParamKey = subtle
1161−
1162− // Content rendering - prepared styles that accept width parameter
1163− s.Tool.ContentLine = muted.Background(bgBaseLighter)
1164− s.Tool.ContentTruncation = muted.Background(bgBaseLighter)
1165− s.Tool.ContentCodeLine = base.Background(bgBase).PaddingLeft(2)
1166− s.Tool.ContentCodeTruncation = muted.Background(bgBase).PaddingLeft(2)
1167− s.Tool.ContentCodeBg = bgBase
1168− s.Tool.Body = base.PaddingLeft(2)
1169−
1170− // Deprecated - kept for backward compatibility
1171− s.Tool.ContentBg = muted.Background(bgBaseLighter)
1172− s.Tool.ContentText = muted
1173− s.Tool.ContentLineNumber = base.Foreground(fgMuted).Background(bgBase).PaddingRight(1).PaddingLeft(1)
1174−
1175− s.Tool.StateWaiting = base.Foreground(fgSubtle)
1176− s.Tool.StateCancelled = base.Foreground(fgSubtle)
1177−
1178− s.Tool.ErrorTag = base.Padding(0, 1).Background(red).Foreground(white)
1179− s.Tool.ErrorMessage = base.Foreground(fgHalfMuted)
1180−
1181− // Diff and multi-edit styles
1182− s.Tool.DiffTruncation = muted.Background(bgBaseLighter).PaddingLeft(2)
1183− s.Tool.NoteTag = base.Padding(0, 1).Background(info).Foreground(white)
1184− s.Tool.NoteMessage = base.Foreground(fgHalfMuted)
1185−
1186− // Job header styles
1187− s.Tool.JobIconPending = base.Foreground(greenDark)
1188− s.Tool.JobIconError = base.Foreground(redDark)
1189− s.Tool.JobIconSuccess = base.Foreground(green)
1190− s.Tool.JobToolName = base.Foreground(blue)
1191− s.Tool.JobAction = base.Foreground(blueDark)
1192− s.Tool.JobPID = muted
1193− s.Tool.JobDescription = subtle
1194−
1195− // Agent task styles
1196− s.Tool.AgentTaskTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(blueLight).Foreground(white)
1197− s.Tool.AgentPrompt = muted
1198−
1199− // Agentic fetch styles
1200− s.Tool.AgenticFetchPromptTag = base.Bold(true).Padding(0, 1).MarginLeft(2).Background(green).Foreground(border)
1201−
1202− // Todo styles
1203− s.Tool.TodoRatio = base.Foreground(blueDark)
1204− s.Tool.TodoCompletedIcon = base.Foreground(green)
1205− s.Tool.TodoInProgressIcon = base.Foreground(greenDark)
1206− s.Tool.TodoPendingIcon = base.Foreground(fgMuted)
1207− s.Tool.TodoStatusNote = lipgloss.NewStyle().Foreground(fgSubtle)
1208− s.Tool.TodoItem = lipgloss.NewStyle().Foreground(fgBase)
1209− s.Tool.TodoJustStarted = lipgloss.NewStyle().Foreground(fgBase)
1210−
1211− // MCP styles
1212− s.Tool.MCPName = base.Foreground(blue)
1213− s.Tool.MCPToolName = base.Foreground(blueDark)
1214− s.Tool.MCPArrow = base.Foreground(blue).SetString(ArrowRightIcon)
1215−
1216− // Loading indicators for images, skills
1217− s.Tool.ResourceLoadedText = base.Foreground(green)
1218− s.Tool.ResourceLoadedIndicator = base.Foreground(greenDark)
1219− s.Tool.ResourceName = base
1220− s.Tool.MediaType = base
1221− s.Tool.ResourceSize = base.Foreground(fgMuted)
1222−
1223− // Tool-call action verbs and result-list styling.
1224− s.Tool.ActionCreate = lipgloss.NewStyle().Foreground(greenLight)
1225− s.Tool.ActionDestroy = lipgloss.NewStyle().Foreground(red)
1226− s.Tool.ResultEmpty = lipgloss.NewStyle().Foreground(fgSubtle)
1227− s.Tool.ResultTruncation = lipgloss.NewStyle().Foreground(fgSubtle)
1228− s.Tool.ResultItemName = lipgloss.NewStyle().Foreground(fgBase)
1229− s.Tool.ResultItemDesc = lipgloss.NewStyle().Foreground(fgSubtle)
1230−
1231− // Hook styles
1232− s.Tool.HookLabel = base.Foreground(green)
1233− s.Tool.HookName = base
1234− s.Tool.HookMatcher = base.Foreground(fgMuted)
1235− s.Tool.HookArrow = base.Foreground(green)
1236− s.Tool.HookDetail = base.Foreground(fgMuted)
1237− s.Tool.HookOK = base.Foreground(charmtone.Guac)
1238− s.Tool.HookDenied = base.Foreground(charmtone.Sriracha)
1239− s.Tool.HookDeniedLabel = base.Foreground(red)
1240− s.Tool.HookDeniedReason = base.Foreground(charmtone.Iron)
1241− s.Tool.HookRewrote = base.Foreground(charmtone.Iron)
1242−
1243− // Buttons
1244− s.Button.Focused = lipgloss.NewStyle().Foreground(white).Background(secondary)
1245− s.Button.Blurred = lipgloss.NewStyle().Foreground(fgBase).Background(bgSubtle)
1246−
1247− // Editor
1248− s.Editor.PromptNormalFocused = lipgloss.NewStyle().Foreground(greenDark).SetString("::: ")
1249− s.Editor.PromptNormalBlurred = s.Editor.PromptNormalFocused.Foreground(fgMuted)
1250− s.Editor.PromptYoloIconFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Oyster).Background(charmtone.Citron).Bold(true).SetString(" ! ")
1251− s.Editor.PromptYoloIconBlurred = s.Editor.PromptYoloIconFocused.Foreground(charmtone.Pepper).Background(charmtone.Squid)
1252− s.Editor.PromptYoloDotsFocused = lipgloss.NewStyle().MarginRight(1).Foreground(charmtone.Zest).SetString(":::")
1253− s.Editor.PromptYoloDotsBlurred = s.Editor.PromptYoloDotsFocused.Foreground(charmtone.Squid)
1254−
1255− s.Radio.On = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOn)
1256− s.Radio.Off = lipgloss.NewStyle().Foreground(fgHalfMuted).SetString(RadioOff)
1257− s.Radio.Label = lipgloss.NewStyle().Foreground(fgHalfMuted)
1258−
1259− // Logo
1260− s.Logo.FieldColor = primary
1261− s.Logo.TitleColorA = secondary
1262− s.Logo.TitleColorB = primary
1263− s.Logo.CharmColor = secondary
1264− s.Logo.VersionColor = primary
1265− s.Logo.SmallCharm = lipgloss.NewStyle().Foreground(secondary)
1266− s.Logo.SmallDiagonals = lipgloss.NewStyle().Foreground(primary)
1267− s.Logo.GradCanvas = lipgloss.NewStyle()
1268− s.Logo.SmallGradFromColor = secondary
1269− s.Logo.SmallGradToColor = primary
1270−
1271− // Section
1272− s.Section.Title = subtle
1273− s.Section.Line = base.Foreground(charmtone.Charcoal)
1274−
1275− // Initialize
1276− s.Initialize.Header = base
1277− s.Initialize.Content = muted
1278− s.Initialize.Accent = base.Foreground(greenDark)
1279−
1280− // ResourceGroup (LSP/MCP/skills sidebar lists).
1281− s.Resource.Heading = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1282− s.Resource.Name = lipgloss.NewStyle().Foreground(charmtone.Squid)
1283− s.Resource.StatusText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1284− s.Resource.OfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Iron).SetString("●")
1285− s.Resource.BusyIcon = s.Resource.OfflineIcon.Foreground(charmtone.Citron)
1286− s.Resource.ErrorIcon = s.Resource.OfflineIcon.Foreground(charmtone.Coral)
1287− s.Resource.OnlineIcon = s.Resource.OfflineIcon.Foreground(charmtone.Guac)
1288− s.Resource.DisabledIcon = lipgloss.NewStyle().Foreground(fgMuted).SetString("●")
1289− s.Resource.AdditionalText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
1290− s.Resource.CapabilityCount = lipgloss.NewStyle().Foreground(fgSubtle)
1291− s.Resource.RowTitleBase = lipgloss.NewStyle().Foreground(fgBase)
1292− s.Resource.RowDescBase = lipgloss.NewStyle().Foreground(fgBase)
1293− s.Resource.DefaultTitleFg = fgMuted
1294− s.Resource.DefaultDescFg = fgSubtle
1295−
1296− // LSP
1297− s.LSP.ErrorDiagnostic = base.Foreground(redDark)
1298− s.LSP.WarningDiagnostic = base.Foreground(warning)
1299− s.LSP.HintDiagnostic = base.Foreground(fgHalfMuted)
1300− s.LSP.InfoDiagnostic = base.Foreground(info)
1301−
1302− // Files
1303− s.Files.Path = lipgloss.NewStyle().Foreground(fgMuted)
1304− s.Files.Additions = lipgloss.NewStyle().Foreground(greenDark)
1305− s.Files.Deletions = lipgloss.NewStyle().Foreground(redDark)
1306− s.Files.SectionTitle = lipgloss.NewStyle().Foreground(fgSubtle)
1307− s.Files.EmptyMessage = lipgloss.NewStyle().Foreground(fgSubtle)
1308− s.Files.TruncationHint = lipgloss.NewStyle().Foreground(fgSubtle)
1309−
1310− // Sidebar
1311− s.Sidebar.SessionTitle = lipgloss.NewStyle().Foreground(fgMuted)
1312− s.Sidebar.WorkingDir = lipgloss.NewStyle().Foreground(fgMuted)
1313−
1314− // ModelInfo
1315− s.ModelInfo.Icon = lipgloss.NewStyle().Foreground(fgSubtle)
1316− s.ModelInfo.Name = lipgloss.NewStyle().Foreground(fgBase)
1317− s.ModelInfo.Provider = lipgloss.NewStyle().Foreground(fgMuted)
1318− s.ModelInfo.ProviderFallback = lipgloss.NewStyle().Foreground(fgMuted).PaddingLeft(2)
1319− s.ModelInfo.Reasoning = lipgloss.NewStyle().Foreground(fgSubtle).PaddingLeft(2)
1320− s.ModelInfo.TokenCount = lipgloss.NewStyle().Foreground(fgSubtle)
1321− s.ModelInfo.TokenPercentage = lipgloss.NewStyle().Foreground(fgMuted)
1322− s.ModelInfo.Cost = lipgloss.NewStyle().Foreground(fgMuted)
1323−
1324− // ResourceGroup
1325− s.Resource.DefaultTitleFg = fgMuted
1326− s.Resource.DefaultDescFg = fgSubtle
1327−
1328− // Chat
1329− messageFocussedBorder := lipgloss.Border{
1330− Left: "▌",
1331− }
1332−
1333− s.Messages.NoContent = lipgloss.NewStyle().Foreground(fgBase)
1334− s.Messages.UserBlurred = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
1335− BorderForeground(primary).BorderStyle(normalBorder)
1336− s.Messages.UserFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
1337− BorderForeground(primary).BorderStyle(messageFocussedBorder)
1338− s.Messages.AssistantBlurred = s.Messages.NoContent.PaddingLeft(2)
1339− s.Messages.AssistantFocused = s.Messages.NoContent.PaddingLeft(1).BorderLeft(true).
1340− BorderForeground(greenDark).BorderStyle(messageFocussedBorder)
1341− s.Messages.Thinking = lipgloss.NewStyle().MaxHeight(10)
1342− s.Messages.ErrorTag = lipgloss.NewStyle().Padding(0, 1).
1343− Background(red).Foreground(white)
1344− s.Messages.ErrorTitle = lipgloss.NewStyle().Foreground(fgHalfMuted)
1345− s.Messages.ErrorDetails = lipgloss.NewStyle().Foreground(fgSubtle)
1346−
1347− // Message item styles
1348− s.Messages.ToolCallFocused = muted.PaddingLeft(1).
1349− BorderStyle(messageFocussedBorder).
1350− BorderLeft(true).
1351− BorderForeground(greenDark)
1352− s.Messages.ToolCallBlurred = muted.PaddingLeft(2)
1353− // No padding or border for compact tool calls within messages
1354− s.Messages.ToolCallCompact = muted
1355− s.Messages.SectionHeader = base.PaddingLeft(2)
1356− s.Messages.AssistantInfoIcon = subtle
1357− s.Messages.AssistantInfoModel = muted
1358− s.Messages.AssistantInfoProvider = subtle
1359− s.Messages.AssistantInfoDuration = subtle
1360− s.Messages.AssistantCanceled = lipgloss.NewStyle().Foreground(fgBase).Italic(true)
1361−
1362− // Thinking section styles
1363− s.Messages.ThinkingBox = subtle.Background(bgBaseLighter)
1364− s.Messages.ThinkingTruncationHint = muted
1365− s.Messages.ThinkingFooterTitle = muted
1366− s.Messages.ThinkingFooterDuration = subtle
1367−
1368− // Text selection.
1369− s.TextSelection = lipgloss.NewStyle().Foreground(charmtone.Salt).Background(charmtone.Charple)
1370−
1371− // Dialog styles
1372− s.Dialog.Title = base.Padding(0, 1).Foreground(primary)
1373− s.Dialog.TitleText = base.Foreground(primary)
1374− s.Dialog.TitleError = base.Foreground(red)
1375− s.Dialog.TitleAccent = base.Foreground(green).Bold(true)
1376− s.Dialog.TitleLineBase = lipgloss.NewStyle()
1377− s.Dialog.TitleGradFromColor = primary
1378− s.Dialog.TitleGradToColor = secondary
1379−
1380− // Dialog.ListItem (commands, reasoning, models)
1381− s.Dialog.ListItem.InfoBlurred = lipgloss.NewStyle().Foreground(fgBase)
1382− s.Dialog.ListItem.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
1383−
1384− // Dialog.Models
1385− s.Dialog.Models.ConfiguredText = lipgloss.NewStyle().Foreground(fgSubtle)
1386−
1387− // Dialog.Permissions
1388− s.Dialog.Permissions.KeyText = lipgloss.NewStyle().Foreground(fgMuted)
1389− s.Dialog.Permissions.ValueText = lipgloss.NewStyle().Foreground(fgBase)
1390− s.Dialog.Permissions.ParamsBg = bgSubtle
1391−
1392− // Dialog.Quit
1393− s.Dialog.Quit.Content = lipgloss.NewStyle().Foreground(fgBase)
1394− s.Dialog.Quit.Frame = lipgloss.NewStyle().BorderForeground(borderFocus).Border(lipgloss.RoundedBorder()).Padding(1, 2)
1395− s.Dialog.View = base.Border(lipgloss.RoundedBorder()).BorderForeground(borderFocus)
1396− s.Dialog.PrimaryText = base.Padding(0, 1).Foreground(primary)
1397− s.Dialog.SecondaryText = base.Padding(0, 1).Foreground(fgSubtle)
1398− s.Dialog.HelpView = base.Padding(0, 1).AlignHorizontal(lipgloss.Left)
1399− s.Dialog.Help.ShortKey = base.Foreground(fgMuted)
1400− s.Dialog.Help.ShortDesc = base.Foreground(fgSubtle)
1401− s.Dialog.Help.ShortSeparator = base.Foreground(border)
1402− s.Dialog.Help.Ellipsis = base.Foreground(border)
1403− s.Dialog.Help.FullKey = base.Foreground(fgMuted)
1404− s.Dialog.Help.FullDesc = base.Foreground(fgSubtle)
1405− s.Dialog.Help.FullSeparator = base.Foreground(border)
1406− s.Dialog.NormalItem = base.Padding(0, 1).Foreground(fgBase)
1407− s.Dialog.SelectedItem = base.Padding(0, 1).Background(primary).Foreground(fgBase)
1408− s.Dialog.InputPrompt = base.Margin(1, 1)
1409−
1410− s.Dialog.List = base.Margin(0, 0, 1, 0)
1411− s.Dialog.ContentPanel = base.Background(bgSubtle).Foreground(fgBase).Padding(1, 2)
1412− s.Dialog.Spinner = base.Foreground(secondary)
1413− s.Dialog.ScrollbarThumb = base.Foreground(secondary)
1414− s.Dialog.ScrollbarTrack = base.Foreground(border)
1415−
1416− s.Dialog.ImagePreview = lipgloss.NewStyle().Padding(0, 1).Foreground(fgSubtle)
1417−
1418− // API key input dialog
1419− s.Dialog.APIKey.Spinner = base.Foreground(green)
1420−
1421− // OAuth dialog
1422− s.Dialog.OAuth.Spinner = base.Foreground(greenLight)
1423− s.Dialog.OAuth.Instructions = lipgloss.NewStyle().Foreground(white)
1424− s.Dialog.OAuth.UserCode = lipgloss.NewStyle().Bold(true).Foreground(white)
1425− s.Dialog.OAuth.Success = lipgloss.NewStyle().Foreground(greenLight)
1426− s.Dialog.OAuth.Link = lipgloss.NewStyle().Foreground(greenDark).Underline(true)
1427− s.Dialog.OAuth.Enter = lipgloss.NewStyle().Foreground(primary)
1428− s.Dialog.OAuth.ErrorText = lipgloss.NewStyle().Foreground(error)
1429− s.Dialog.OAuth.StatusText = lipgloss.NewStyle().Foreground(fgMuted)
1430− s.Dialog.OAuth.UserCodeBg = bgBaseLighter
1431−
1432− s.Dialog.Arguments.Content = base.Padding(1)
1433− s.Dialog.Arguments.Description = base.MarginBottom(1).MaxHeight(3)
1434− s.Dialog.Arguments.InputLabelBlurred = base.Foreground(fgMuted)
1435− s.Dialog.Arguments.InputLabelFocused = base.Bold(true)
1436− s.Dialog.Arguments.InputRequiredMarkBlurred = base.Foreground(fgMuted).SetString("*")
1437− s.Dialog.Arguments.InputRequiredMarkFocused = base.Foreground(primary).Bold(true).SetString("*")
1438−
1439− s.Dialog.Sessions.DeletingTitle = s.Dialog.Title.Foreground(red)
1440− s.Dialog.Sessions.DeletingView = s.Dialog.View.BorderForeground(red)
1441− s.Dialog.Sessions.DeletingMessage = base.Padding(1)
1442− s.Dialog.Sessions.DeletingTitleGradientFromColor = red
1443− s.Dialog.Sessions.DeletingTitleGradientToColor = primary
1444− s.Dialog.Sessions.DeletingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
1445− s.Dialog.Sessions.DeletingItemFocused = s.Dialog.SelectedItem.Background(red).Foreground(charmtone.Butter)
1446−
1447− s.Dialog.Sessions.RenamingingTitle = s.Dialog.Title.Foreground(charmtone.Zest)
1448− s.Dialog.Sessions.RenamingView = s.Dialog.View.BorderForeground(charmtone.Zest)
1449− s.Dialog.Sessions.RenamingingMessage = base.Padding(1)
1450− s.Dialog.Sessions.RenamingTitleGradientFromColor = charmtone.Zest
1451− s.Dialog.Sessions.RenamingTitleGradientToColor = charmtone.Bok
1452− s.Dialog.Sessions.RenamingItemBlurred = s.Dialog.NormalItem.Foreground(fgSubtle)
1453− s.Dialog.Sessions.RenamingingItemFocused = s.Dialog.SelectedItem.UnsetBackground().UnsetForeground()
1454− s.Dialog.Sessions.RenamingPlaceholder = base.Foreground(charmtone.Squid)
1455− s.Dialog.Sessions.InfoBlurred = lipgloss.NewStyle().Foreground(fgSubtle)
1456− s.Dialog.Sessions.InfoFocused = lipgloss.NewStyle().Foreground(fgBase)
1457−
1458− s.Status.Help = lipgloss.NewStyle().Padding(0, 1)
1459− s.Status.SuccessIndicator = base.Foreground(bgSubtle).Background(green).Padding(0, 1).Bold(true).SetString("OKAY!")
1460− s.Status.InfoIndicator = s.Status.SuccessIndicator
1461− s.Status.UpdateIndicator = s.Status.SuccessIndicator.SetString("HEY!")
1462− s.Status.WarnIndicator = s.Status.SuccessIndicator.Foreground(bgOverlay).Background(yellow).SetString("WARNING")
1463− s.Status.ErrorIndicator = s.Status.SuccessIndicator.Foreground(bgBase).Background(red).SetString("ERROR")
1464− s.Status.SuccessMessage = base.Foreground(bgSubtle).Background(greenDark).Padding(0, 1)
1465− s.Status.InfoMessage = s.Status.SuccessMessage
1466− s.Status.UpdateMessage = s.Status.SuccessMessage
1467− s.Status.WarnMessage = s.Status.SuccessMessage.Foreground(bgOverlay).Background(warning)
1468− s.Status.ErrorMessage = s.Status.SuccessMessage.Foreground(white).Background(redDark)
1469−
1470− // Completions styles
1471− s.Completions.Normal = base.Background(bgSubtle).Foreground(fgBase)
1472− s.Completions.Focused = base.Background(primary).Foreground(white)
1473− s.Completions.Match = base.Underline(true)
1474−
1475− // Attachments styles
1476− attachmentIconStyle := base.Foreground(bgSubtle).Background(green).Padding(0, 1)
1477− s.Attachments.Image = attachmentIconStyle.SetString(ImageIcon)
1478− s.Attachments.Text = attachmentIconStyle.SetString(TextIcon)
1479− s.Attachments.Normal = base.Padding(0, 1).MarginRight(1).Background(fgMuted).Foreground(fgBase)
1480− s.Attachments.Deleting = base.Padding(0, 1).Bold(true).Background(red).Foreground(fgBase)
1481−
1482− // Pills styles
1483− s.Pills.Base = base.Padding(0, 1)
1484− s.Pills.Focused = base.Padding(0, 1).BorderStyle(lipgloss.RoundedBorder()).BorderForeground(bgOverlay)
1485− s.Pills.Blurred = base.Padding(0, 1).BorderStyle(lipgloss.HiddenBorder())
1486− s.Pills.QueueItemPrefix = lipgloss.NewStyle().Foreground(fgMuted).SetString(" •")
1487− s.Pills.QueueItemText = lipgloss.NewStyle().Foreground(fgMuted)
1488− s.Pills.QueueLabel = lipgloss.NewStyle().Foreground(fgBase)
1489− s.Pills.QueueIconBase = lipgloss.NewStyle().Foreground(fgBase)
1490− s.Pills.QueueGradFromColor = redDark
1491− s.Pills.QueueGradToColor = secondary
1492− s.Pills.TodoLabel = lipgloss.NewStyle().Foreground(fgBase)
1493− s.Pills.TodoProgress = lipgloss.NewStyle().Foreground(fgMuted)
1494− s.Pills.TodoCurrentTask = lipgloss.NewStyle().Foreground(fgSubtle)
1495− s.Pills.TodoSpinner = lipgloss.NewStyle().Foreground(greenDark)
1496− s.Pills.HelpKey = lipgloss.NewStyle().Foreground(fgMuted)
1497− s.Pills.HelpText = lipgloss.NewStyle().Foreground(fgSubtle)
1498− s.Pills.Area = base
1499−
1500− return s
569+// hex returns a pointer to the "#rrggbb" representation of c. It's used to
570+// satisfy glamour's string-pointer API when configuring markdown colors
571+// from the theme palette.
572+func hex(c color.Color) *string {
573+ r, g, b, _ := c.RGBA()
574+ s := fmt.Sprintf("#%02x%02x%02x", r>>8, g>>8, b>>8)
575+ return &s
1501576}
1502577
1503578func chromaStyle(style ansi.StylePrimitive) string {