From a7addf127e7ae529f8a18e26376ffd0566fba81b Mon Sep 17 00:00:00 2001 From: lucaronin Date: Sat, 21 Feb 2026 13:14:18 +0100 Subject: [PATCH 1/4] design: color & icon customization for types wireframes Four frames added to design/color-icon-for-types.pen: 1. Right-click context menu on sidebar section group headers 2. Icon & color picker popover (color swatches + icon grid) 3. Sidebar with custom icons & colors applied 4. Frontmatter persistence model (icon/color in type YAML) Co-Authored-By: Claude Opus 4.6 --- design/color-icon-for-types.pen | 5381 +++++++++++++++++++++++++++++++ 1 file changed, 5381 insertions(+) create mode 100644 design/color-icon-for-types.pen diff --git a/design/color-icon-for-types.pen b/design/color-icon-for-types.pen new file mode 100644 index 00000000..2607c47b --- /dev/null +++ b/design/color-icon-for-types.pen @@ -0,0 +1,5381 @@ +{ + "version": "2.8", + "children": [ + { + "type": "frame", + "id": "qHhaj", + "x": 68, + "y": 3385, + "name": "Laputa App — Full Layout (Light)", + "theme": { + "Mode": "Light" + }, + "clip": true, + "width": 1440, + "height": 900, + "fill": "$--background", + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "yVQFF", + "name": "macOS Title Bar", + "width": "fill_container", + "height": 38, + "fill": "$--sidebar", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "$--border" + }, + "gap": 12, + "padding": [ + 0, + 12 + ], + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "0MCME", + "name": "trafficLights", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "ellipse", + "id": "c2AU6", + "name": "closeBtn", + "fill": "#FF5F57", + "width": 12, + "height": 12 + }, + { + "type": "ellipse", + "id": "h0zbh", + "name": "minimizeBtn", + "fill": "#FEBC2E", + "width": 12, + "height": 12 + }, + { + "type": "ellipse", + "id": "tcqbu", + "name": "maximizeBtn", + "fill": "#28C840", + "width": 12, + "height": 12 + } + ] + } + ] + }, + { + "type": "frame", + "id": "oHDfa", + "name": "Content", + "width": "fill_container", + "height": "fill_container", + "children": [ + { + "type": "frame", + "id": "ZTOSJ", + "name": "Panel: Sidebar", + "clip": true, + "width": 250, + "height": "fill_container", + "fill": "$--sidebar", + "stroke": { + "align": "inside", + "thickness": 0, + "fill": "$--sidebar-border" + }, + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "4wMva", + "name": "Filters", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 1, + "padding": [ + 8, + 8, + 16, + 8 + ], + "children": [ + { + "type": "frame", + "id": "Q78hg", + "name": "filterAll", + "width": "fill_container", + "fill": "#4a9eff18", + "cornerRadius": 6, + "gap": 8, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "5ip58", + "name": "leftAll", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "PXpWi", + "name": "iconAll", + "width": 18, + "height": 18, + "iconFontName": "tray-fill", + "iconFontFamily": "phosphor", + "weight": 700, + "fill": "$--primary" + }, + { + "type": "text", + "id": "NWprl", + "name": "fAllTxt", + "fill": "$--primary", + "content": "Untagged", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "600" + } + ] + }, + { + "type": "frame", + "id": "NZ9Dv", + "name": "countAll", + "height": 20, + "fill": "$--primary", + "cornerRadius": 9999, + "padding": [ + 0, + 6 + ], + "justifyContent": "center", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "mPlUV", + "name": "badgeAllTxt", + "fill": "$--primary-foreground", + "content": "24", + "fontFamily": "Inter", + "fontSize": 10, + "fontWeight": "600" + } + ] + } + ] + }, + { + "type": "frame", + "id": "YLWsY", + "name": "filterUntagged", + "width": "fill_container", + "cornerRadius": 6, + "gap": 8, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "k7LIr", + "name": "leftUntagged", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "FMVlh", + "name": "untaggedIcon", + "width": 18, + "height": 18, + "iconFontName": "cardholder", + "iconFontFamily": "phosphor", + "weight": 700, + "fill": "$--foreground" + }, + { + "type": "text", + "id": "5XllD", + "name": "untaggedTxt", + "fill": "$--foreground", + "content": "All Notes", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "aSpGv", + "name": "countUntagged", + "height": 20, + "fill": "$--secondary", + "cornerRadius": 9999, + "padding": [ + 0, + 6 + ], + "justifyContent": "center", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "qZUFt", + "name": "untaggedBadgeTxt", + "fill": "$--muted-foreground", + "content": "6", + "fontFamily": "Inter", + "fontSize": 10, + "fontWeight": "600" + } + ] + } + ] + }, + { + "type": "frame", + "id": "Jahon", + "name": "filterTrash", + "width": "fill_container", + "cornerRadius": 6, + "gap": 8, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "IOiBI", + "name": "leftTrash", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "X6F74", + "name": "trashIcon", + "width": 18, + "height": 18, + "iconFontName": "trash", + "iconFontFamily": "phosphor", + "weight": 700, + "fill": "$--foreground" + }, + { + "type": "text", + "id": "vRMH9", + "name": "trashTxt", + "fill": "$--foreground", + "content": "Archive", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "JMpkq", + "name": "countTrash", + "height": 20, + "fill": "$--secondary", + "cornerRadius": 9999, + "padding": [ + 0, + 6 + ], + "justifyContent": "center", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "0cKm4", + "name": "trashBadgeTxt", + "fill": "$--muted-foreground", + "content": "2", + "fontFamily": "Inter", + "fontSize": 10, + "fontWeight": "600" + } + ] + } + ] + }, + { + "type": "frame", + "id": "6RMbq", + "name": "filterChanges", + "width": "fill_container", + "cornerRadius": 6, + "gap": 6, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "DpdBu", + "name": "leftChanges", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "VTusA", + "name": "iconChanges", + "width": 18, + "height": 18, + "iconFontName": "git-branch", + "iconFontFamily": "phosphor", + "weight": 700, + "fill": "$--foreground" + }, + { + "type": "text", + "id": "qvq5O", + "name": "fChangesTxt", + "fill": "$--foreground", + "content": "Changes", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "qAMES", + "name": "countChanges", + "height": 20, + "fill": "$--secondary", + "cornerRadius": 9999, + "padding": [ + 0, + 6 + ], + "justifyContent": "center", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "nkzVk", + "name": "badgeChangesTxt", + "fill": "$--muted-foreground", + "content": "3", + "fontFamily": "Inter", + "fontSize": 10, + "fontWeight": "600" + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "SSEUP", + "name": "Sections", + "width": "fill_container", + "height": "fill_container", + "layout": "vertical", + "padding": [ + 8, + 0 + ], + "children": [ + { + "type": "frame", + "id": "nM2Cn", + "name": "projGroup", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": { + "type": "color", + "color": "$--border", + "enabled": false + } + }, + "layout": "vertical", + "gap": 2, + "padding": [ + 4, + 6, + 8, + 6 + ], + "children": [ + { + "type": "frame", + "id": "ouIl9", + "name": "projHeader", + "width": "fill_container", + "cornerRadius": 4, + "gap": 8, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "Rx1Cp", + "name": "leftProj", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "t5WJJ", + "name": "projIcon", + "width": 18, + "height": 18, + "iconFontName": "wrench", + "iconFontFamily": "phosphor", + "weight": 700, + "fill": "$--accent-red" + }, + { + "type": "text", + "id": "S3sGC", + "name": "projLabel", + "fill": "$--foreground", + "content": "Projects", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + } + ] + }, + { + "type": "icon_font", + "id": "FKwSS", + "name": "projChev", + "width": 14, + "height": 14, + "iconFontName": "chevron-down", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + } + ] + }, + { + "type": "frame", + "id": "IHzFh", + "name": "projItem1", + "width": "fill_container", + "fill": "$--accent-red-light", + "cornerRadius": 6, + "padding": [ + 6, + 16, + 6, + 28 + ], + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "iiFcl", + "name": "proj1Txt", + "fill": "$--accent-red", + "content": "Laputa App", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "LdaU9", + "name": "projItem2", + "width": "fill_container", + "cornerRadius": 6, + "padding": [ + 6, + 16, + 6, + 28 + ], + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "XRll4", + "name": "proj2Txt", + "fill": "$--muted-foreground", + "content": "Portfolio Rewrite", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "Vmykd", + "name": "projItem3", + "width": "fill_container", + "cornerRadius": 6, + "padding": [ + 6, + 16, + 6, + 28 + ], + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "0PqQQ", + "name": "proj3Txt", + "fill": "$--muted-foreground", + "content": "AI Habit Tracker", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "WAQtn", + "name": "respGroup", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": { + "type": "color", + "color": "$--border", + "enabled": false + } + }, + "layout": "vertical", + "gap": 2, + "padding": [ + 4, + 6 + ], + "children": [ + { + "type": "frame", + "id": "aSi3l", + "name": "respHeader", + "width": "fill_container", + "cornerRadius": 4, + "gap": 8, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "lov6B", + "name": "leftResp", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "Em4ns", + "name": "respIcon", + "width": 18, + "height": 18, + "iconFontName": "medal", + "iconFontFamily": "phosphor", + "weight": 700, + "fill": "$--accent-purple" + }, + { + "type": "text", + "id": "WPRCo", + "name": "respLabel", + "fill": "$--foreground", + "content": "Responsibilities", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + } + ] + }, + { + "type": "icon_font", + "id": "6hDdQ", + "name": "respChev", + "width": 14, + "height": 14, + "iconFontName": "chevron-right", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + } + ] + } + ] + }, + { + "type": "frame", + "id": "FmUu0", + "name": "procGroup", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": { + "type": "color", + "color": "$--border", + "enabled": false + } + }, + "layout": "vertical", + "gap": 2, + "padding": [ + 4, + 6 + ], + "children": [ + { + "type": "frame", + "id": "Dat6o", + "name": "respHeader", + "width": "fill_container", + "cornerRadius": 4, + "gap": 8, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "iJlgx", + "name": "leftResp", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "I2J1R", + "name": "respIcon", + "width": 18, + "height": 18, + "iconFontName": "arrows-clockwise", + "iconFontFamily": "phosphor", + "weight": 700, + "fill": "$--accent-purple" + }, + { + "type": "text", + "id": "AToF0", + "name": "Procedures", + "fill": "$--foreground", + "content": "Procedures", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + } + ] + }, + { + "type": "icon_font", + "id": "meEIL", + "name": "procChev", + "width": 14, + "height": 14, + "iconFontName": "chevron-right", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + } + ] + } + ] + }, + { + "type": "frame", + "id": "T4NG2", + "name": "peopleGroup", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": { + "type": "color", + "color": "$--border", + "enabled": false + } + }, + "layout": "vertical", + "gap": 2, + "padding": [ + 4, + 6 + ], + "children": [ + { + "type": "frame", + "id": "Q01iL", + "name": "peopleHeader", + "width": "fill_container", + "cornerRadius": 4, + "gap": 8, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "eE6Ca", + "name": "leftPeople", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "jOmVa", + "name": "peopleIcon", + "width": 18, + "height": 18, + "iconFontName": "users", + "iconFontFamily": "phosphor", + "weight": 700, + "fill": "$--accent-yellow" + }, + { + "type": "text", + "id": "caelD", + "name": "peopleLbl", + "fill": "$--foreground", + "content": "People", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + } + ] + }, + { + "type": "icon_font", + "id": "TkuS1", + "name": "peopleChev", + "width": 14, + "height": 14, + "iconFontName": "chevron-right", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + } + ] + } + ] + }, + { + "type": "frame", + "id": "BY1Qx", + "name": "eventsGroup", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": { + "type": "color", + "color": "$--border", + "enabled": false + } + }, + "layout": "vertical", + "gap": 2, + "padding": [ + 4, + 6 + ], + "children": [ + { + "type": "frame", + "id": "v28d8", + "name": "eventsHeader", + "width": "fill_container", + "cornerRadius": 4, + "gap": 8, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "TPyOc", + "name": "leftEvents", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "ELRMi", + "name": "eventsIcon", + "width": 18, + "height": 18, + "iconFontName": "calendar-blank", + "iconFontFamily": "phosphor", + "weight": 700, + "fill": "$--accent-yellow" + }, + { + "type": "text", + "id": "OlI6Q", + "name": "eventsLbl", + "fill": "$--foreground", + "content": "Events", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + } + ] + }, + { + "type": "icon_font", + "id": "TZwK8", + "name": "eventsChev", + "width": 14, + "height": 14, + "iconFontName": "chevron-right", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + } + ] + } + ] + }, + { + "type": "frame", + "id": "trKNW", + "name": "topicsGroup", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": { + "type": "color", + "color": "$--border", + "enabled": false + } + }, + "layout": "vertical", + "gap": 2, + "padding": [ + 4, + 6 + ], + "children": [ + { + "type": "frame", + "id": "qdbZ3", + "name": "eventsHeader", + "width": "fill_container", + "cornerRadius": 4, + "gap": 8, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "JP83M", + "name": "leftEvents", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "Q57kF", + "name": "eventsIcon", + "width": 18, + "height": 18, + "iconFontName": "tag", + "iconFontFamily": "phosphor", + "weight": 700, + "fill": "$--accent-green" + }, + { + "type": "text", + "id": "DeVxP", + "name": "eventsLbl", + "fill": "$--foreground", + "content": "Topics", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + } + ] + }, + { + "type": "icon_font", + "id": "nzAcm", + "name": "topicsChev", + "width": 14, + "height": 14, + "iconFontName": "chevron-right", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + } + ] + } + ] + }, + { + "type": "frame", + "id": "ZWXuk", + "name": "topicsGroup", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": { + "type": "color", + "color": "$--border", + "enabled": false + } + }, + "layout": "vertical", + "gap": 2, + "padding": [ + 4, + 6 + ], + "children": [ + { + "type": "frame", + "id": "CQaLg", + "name": "eventsHeader", + "width": "fill_container", + "cornerRadius": 4, + "gap": 8, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "2SMcN", + "name": "leftEvents", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "0N8UH", + "name": "eventsIcon", + "width": 18, + "height": 18, + "iconFontName": "leaf", + "iconFontFamily": "phosphor", + "weight": 700, + "fill": "$--accent-green" + }, + { + "type": "text", + "id": "6XB2H", + "name": "eventsLbl", + "fill": "$--foreground", + "content": "Notes", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + } + ] + }, + { + "type": "icon_font", + "id": "NmBSB", + "name": "topicsChev", + "width": 14, + "height": 14, + "iconFontName": "chevron-right", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + } + ] + }, + { + "type": "frame", + "id": "2cQBp", + "name": "eventsHeader", + "width": "fill_container", + "cornerRadius": 4, + "gap": 8, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "0f10v", + "name": "leftEvents", + "gap": 8, + "padding": [ + 8, + 0 + ], + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "xBv1X", + "name": "eventsIcon", + "width": 18, + "height": 18, + "iconFontName": "plus", + "iconFontFamily": "phosphor", + "weight": 700, + "fill": "$--muted-foreground" + }, + { + "type": "text", + "id": "viWPL", + "name": "eventsLbl", + "fill": "$--muted-foreground", + "content": "Create new type", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "8lLHp", + "name": "Commit Area", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "top": 1 + }, + "fill": "$--border" + }, + "layout": "vertical", + "padding": 12, + "children": [ + { + "type": "frame", + "id": "YYjuy", + "name": "commitBtn", + "width": "fill_container", + "fill": "$--primary", + "cornerRadius": 6, + "gap": 6, + "padding": [ + 8, + 16 + ], + "justifyContent": "center", + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "YF44G", + "name": "commitIcon", + "width": 14, + "height": 14, + "iconFontName": "git-commit-horizontal", + "iconFontFamily": "lucide", + "fill": "$--primary-foreground" + }, + { + "type": "text", + "id": "vpzJm", + "name": "commitTxt", + "fill": "$--primary-foreground", + "content": "Commit & Push", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + }, + { + "type": "frame", + "id": "vhU5z", + "name": "commitBadge", + "height": 18, + "fill": "#ffffff40", + "cornerRadius": 9, + "padding": [ + 0, + 5 + ], + "justifyContent": "center", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "sdvYT", + "name": "commitBadgeTxt", + "fill": "$--white", + "content": "3", + "fontFamily": "Inter", + "fontSize": 10, + "fontWeight": "600" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "rectangle", + "id": "IIvWr", + "name": "Resize Handle", + "fill": "$--border", + "width": 1, + "height": "fill_container" + }, + { + "type": "frame", + "id": "zFIJv", + "name": "Panel: NoteList", + "clip": true, + "width": 300, + "height": "fill_container", + "fill": "$--card", + "stroke": { + "align": "inside", + "thickness": 0, + "fill": "$--border" + }, + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "uZJVN", + "name": "NoteList Header", + "width": "fill_container", + "height": 45, + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "$--border" + }, + "padding": [ + 14, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "hmbdb", + "name": "nlTitle", + "fill": "$--foreground", + "content": "Laputa App", + "fontFamily": "Inter", + "fontSize": 14, + "fontWeight": "600" + }, + { + "type": "frame", + "id": "m6AeW", + "name": "nlActions", + "gap": 12, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "8gMXE", + "name": "searchIcon", + "width": 16, + "height": 16, + "iconFontName": "magnifying-glass", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + }, + { + "type": "icon_font", + "id": "YqvSN", + "name": "plusIcon", + "width": 16, + "height": 16, + "iconFontName": "plus", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + } + ] + } + ] + }, + { + "type": "frame", + "id": "0aJUm", + "name": "Note Items", + "clip": true, + "width": "fill_container", + "height": "fill_container", + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "y9y05", + "name": "Backlinks Group", + "width": "fill_container", + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "tMsM4", + "name": "Note Item Selected", + "width": "fill_container", + "fill": "$--accent-red-light", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "#E9E9E7" + }, + "children": [ + { + "type": "rectangle", + "id": "npYuc", + "name": "leftAccent", + "fill": "$--accent-red", + "width": 3, + "height": "fill_container" + }, + { + "type": "frame", + "id": "a33Wx", + "name": "noteSelContent", + "width": "fill_container", + "layout": "vertical", + "gap": 4, + "padding": [ + 14, + 16 + ], + "children": [ + { + "type": "frame", + "id": "AZkyx", + "name": "noteSelRow", + "width": "fill_container", + "gap": 8, + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "j7GDJ", + "name": "titleGroup", + "width": "fill_container", + "gap": 6, + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "j7o2Q", + "name": "noteSelTitle", + "fill": "$--foreground", + "content": "Laputa App", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "600" + }, + { + "type": "icon_font", + "id": "BnkTG", + "name": "ico", + "width": 14, + "height": 14, + "iconFontName": "wrench", + "iconFontFamily": "lucide", + "fill": "$--accent-red" + } + ] + } + ] + }, + { + "type": "text", + "id": "Ac5Ds", + "name": "noteSelSnip", + "fill": "$--foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Personal knowledge and life management desktop app...", + "lineHeight": 1.5, + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "9ASsT", + "name": "noteSelTime", + "fill": "$--accent-red", + "content": "2m ago", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "7G8pN", + "name": "Related Notes Group", + "width": "fill_container", + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "fDxtF", + "name": "Related Notes Header", + "width": "fill_container", + "height": 32, + "fill": "$--muted", + "padding": [ + 0, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "UBRdO", + "name": "rnLeft", + "gap": 6, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "8Z4Wq", + "name": "rnLabel", + "fill": "$--muted-foreground", + "content": "RELATED NOTES", + "fontFamily": "IBM Plex Mono", + "fontSize": 11 + }, + { + "type": "text", + "id": "vpnZr", + "name": "rnCount", + "fill": "$--muted-foreground", + "content": "2", + "fontFamily": "IBM Plex Mono", + "fontSize": 11, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "X9LIR", + "name": "rnRight", + "gap": 10, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "fzVWu", + "name": "rnFilter", + "width": 12, + "height": 12, + "iconFontName": "funnel", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + }, + { + "type": "icon_font", + "id": "wuqsO", + "name": "rnPlus", + "width": 12, + "height": 12, + "iconFontName": "plus", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + }, + { + "type": "icon_font", + "id": "SbjLq", + "name": "rnChev", + "width": 12, + "height": 12, + "iconFontName": "chevron-down", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + } + ] + } + ] + }, + { + "type": "frame", + "id": "AsAKG", + "name": "Note Item", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 4, + "padding": [ + 14, + 16 + ], + "children": [ + { + "type": "frame", + "id": "QX5A1", + "name": "note2Row", + "width": "fill_container", + "gap": 8, + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "rYdta", + "name": "leafGroup", + "width": "fill_container", + "gap": 6, + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "PC9XN", + "name": "note2Title", + "fill": "$--foreground", + "content": "Portfolio Rewrite", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + }, + { + "type": "icon_font", + "id": "oI64Y", + "name": "leafIco", + "width": 14, + "height": 14, + "iconFontName": "leaf", + "iconFontFamily": "phosphor", + "fill": "$--accent-green" + } + ] + } + ] + }, + { + "type": "text", + "id": "S9JPj", + "name": "note2Snip", + "fill": "$--muted-foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Redesigning portfolio site with modern stack and updated visual language...", + "lineHeight": 1.5, + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "lU75x", + "name": "note2Time", + "fill": "$--muted-foreground", + "content": "1h ago", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "u6E6Z", + "name": "Note Item", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 4, + "padding": [ + 14, + 16 + ], + "children": [ + { + "type": "frame", + "id": "ZjCz3", + "name": "note2Row", + "width": "fill_container", + "gap": 8, + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "mxCzu", + "name": "leafGroup", + "width": "fill_container", + "gap": 6, + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "INenY", + "name": "note2Title", + "fill": "$--foreground", + "content": "Sample note 2", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + }, + { + "type": "icon_font", + "id": "gz6qS", + "name": "leafIco", + "width": 14, + "height": 14, + "iconFontName": "leaf", + "iconFontFamily": "phosphor", + "fill": "$--accent-green" + } + ] + } + ] + }, + { + "type": "text", + "id": "s141z", + "name": "note2Snip", + "fill": "$--muted-foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Lorem ipsum dolor sit amet consequetur with modern stack and updated language...", + "lineHeight": 1.5, + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "SvGnt", + "name": "note2Time", + "fill": "$--muted-foreground", + "content": "1h ago", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "1GwHB", + "name": "Events Group", + "width": "fill_container", + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "fp4xI", + "name": "Events Header", + "width": "fill_container", + "height": 32, + "fill": "$--muted", + "padding": [ + 0, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "MNNuU", + "name": "evLeft", + "gap": 6, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "BNxZp", + "name": "evLabel", + "fill": "$--muted-foreground", + "content": "EVENTS", + "fontFamily": "IBM Plex Mono", + "fontSize": 11 + }, + { + "type": "text", + "id": "qcEvw", + "name": "evCount", + "fill": "$--muted-foreground", + "content": "2", + "fontFamily": "IBM Plex Sans", + "fontSize": 11, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "WCyfc", + "name": "evRight", + "gap": 10, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "SpCoO", + "name": "evFilter", + "width": 12, + "height": 12, + "iconFontName": "funnel", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + }, + { + "type": "icon_font", + "id": "G1DCl", + "name": "evPlus", + "width": 12, + "height": 12, + "iconFontName": "plus", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + }, + { + "type": "icon_font", + "id": "sAalN", + "name": "evChev", + "width": 12, + "height": 12, + "iconFontName": "chevron-down", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + } + ] + } + ] + }, + { + "type": "frame", + "id": "k7CjA", + "name": "Note Item 2", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 4, + "padding": [ + 14, + 16 + ], + "children": [ + { + "type": "frame", + "id": "og5Gz", + "name": "note3Row", + "width": "fill_container", + "gap": 8, + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "ttBdk", + "name": "calGroup", + "width": "fill_container", + "gap": 6, + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "KI8Bf", + "name": "note3Title", + "fill": "$--foreground", + "content": "Meeting with Grant", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + }, + { + "type": "icon_font", + "id": "LdRjU", + "name": "calIco", + "width": 14, + "height": 14, + "iconFontName": "calendar", + "iconFontFamily": "lucide", + "fill": "$--accent-yellow" + } + ] + } + ] + }, + { + "type": "text", + "id": "FfPXa", + "name": "note3Snip", + "fill": "$--muted-foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Discussed Q1 goals and hiring priorities with the engineering team leads...", + "lineHeight": 1.5, + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "BHh4Z", + "name": "note3Time", + "fill": "$--muted-foreground", + "content": "Yesterday", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "AQ51u", + "name": "Note Item 2", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 4, + "padding": [ + 14, + 16 + ], + "children": [ + { + "type": "frame", + "id": "SHgK3", + "name": "note3Row", + "width": "fill_container", + "gap": 8, + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "3rIet", + "name": "calGroup", + "width": "fill_container", + "gap": 6, + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "dCwzu", + "name": "note3Title", + "fill": "$--foreground", + "content": "Meeting with Matteo", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + }, + { + "type": "icon_font", + "id": "l8O2I", + "name": "calIco", + "width": 14, + "height": 14, + "iconFontName": "calendar", + "iconFontFamily": "lucide", + "fill": "$--accent-yellow" + } + ] + } + ] + }, + { + "type": "text", + "id": "1L4oo", + "name": "note3Snip", + "fill": "$--muted-foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Discussed Q1 goals and hiring priorities with the engineering team leads...", + "lineHeight": 1.5, + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "3oBam", + "name": "note3Time", + "fill": "$--muted-foreground", + "content": "Yesterday", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "rectangle", + "id": "MeqjO", + "name": "Resize Handle 2", + "fill": "$--border", + "width": 1, + "height": "fill_container" + }, + { + "type": "frame", + "id": "zAbPt", + "name": "Panel: Editor", + "clip": true, + "width": "fill_container", + "height": "fill_container", + "fill": "$--background", + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "NdYcO", + "name": "Tab Bar", + "width": "fill_container", + "height": 45, + "fill": "$--sidebar", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "$--sidebar-border" + }, + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "nVUGr", + "name": "Tab Active", + "height": "fill_container", + "fill": "$--background", + "stroke": { + "align": "inside", + "thickness": { + "right": 1 + }, + "fill": "$--border" + }, + "gap": 6, + "padding": [ + 0, + 14 + ], + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "kBpGX", + "name": "tab1Txt", + "fill": "$--foreground", + "content": "Laputa App", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + }, + { + "type": "icon_font", + "id": "Bb2AE", + "name": "tab1Close", + "width": 14, + "height": 14, + "iconFontName": "x", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + } + ] + }, + { + "type": "frame", + "id": "GYeyL", + "name": "Tab Inactive", + "height": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "right": 1, + "bottom": 1 + }, + "fill": "$--sidebar-border" + }, + "gap": 6, + "padding": [ + 0, + 14 + ], + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "kZ1bn", + "name": "tab2Txt", + "fill": "$--muted-foreground", + "content": "Portfolio Rewrite", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "icon_font", + "id": "RwyYI", + "name": "tab2Close", + "opacity": 0, + "width": 14, + "height": 14, + "iconFontName": "x", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + } + ] + }, + { + "type": "frame", + "id": "sbpmq", + "name": "tabSpacer", + "width": "fill_container", + "height": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "$--border" + } + }, + { + "type": "frame", + "id": "WF97k", + "name": "tabControls", + "height": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1, + "left": 1 + }, + "fill": "$--border" + }, + "gap": 12, + "padding": [ + 0, + 12 + ], + "justifyContent": "space_around", + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "rfSoS", + "name": "iconPlus", + "width": 16, + "height": 16, + "iconFontName": "plus", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + }, + { + "type": "icon_font", + "id": "t0u6z", + "name": "iconSplit", + "width": 16, + "height": 16, + "iconFontName": "columns", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + }, + { + "type": "icon_font", + "id": "kbe1P", + "name": "iconExpand", + "width": 16, + "height": 16, + "iconFontName": "arrows-out-simple", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + } + ] + } + ] + }, + { + "type": "frame", + "id": "rTdKS", + "name": "Editor Body", + "width": "fill_container", + "height": "fill_container", + "children": [ + { + "type": "frame", + "id": "NfyTF", + "name": "Editor Left", + "width": "fill_container", + "height": "fill_container", + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "hvoFh", + "name": "Info Bar", + "width": "fill_container", + "height": 45, + "fill": "$--background", + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "$--border" + }, + "padding": [ + 6, + 16 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "bTP0I", + "name": "infoLeft", + "gap": 6, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "Msj7v", + "name": "breadcrumbType", + "fill": "$--muted-foreground", + "content": "Project", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "wqdMG", + "name": "breadcrumbSep", + "fill": "$--muted-foreground", + "content": "›", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "FzENd", + "name": "breadcrumbName", + "fill": "$--foreground", + "content": "Laputa App", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + }, + { + "type": "text", + "id": "pvTmc", + "name": "dotSep", + "fill": "$--muted-foreground", + "content": "·", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "9ga1R", + "name": "modifiedTxt", + "fill": "$--accent-yellow", + "content": "M", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "600" + } + ] + }, + { + "type": "frame", + "id": "3bzDp", + "name": "infoRight", + "gap": 12, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "9Ctb0", + "name": "iconSearch", + "width": 16, + "height": 16, + "iconFontName": "magnifying-glass", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + }, + { + "type": "icon_font", + "id": "csHzz", + "name": "iconSearch", + "width": 16, + "height": 16, + "iconFontName": "git-branch", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + }, + { + "type": "icon_font", + "id": "2s6Of", + "name": "iconSearch", + "width": 16, + "height": 16, + "iconFontName": "cursor-text", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + }, + { + "type": "icon_font", + "id": "JVSlj", + "name": "iconAI", + "width": 16, + "height": 16, + "iconFontName": "sparkle", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + }, + { + "type": "icon_font", + "id": "k3AiV", + "name": "iconMore", + "width": 16, + "height": 16, + "iconFontName": "dots-three", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + } + ] + } + ] + }, + { + "type": "frame", + "id": "vvETz", + "name": "Editor Content", + "width": "fill_container", + "height": "fill_container", + "layout": "vertical", + "gap": 16, + "padding": [ + 32, + 64 + ], + "children": [ + { + "type": "text", + "id": "b6up3", + "name": "editorP1", + "fill": "$--foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Personal knowledge and life management desktop app, built with Tauri v2 + React + TypeScript + CodeMirror 6. It reads a vault of markdown files with YAML frontmatter and presents them in a four-panel UI inspired by Bear Notes.", + "lineHeight": 1.6, + "fontFamily": "Inter", + "fontSize": 16, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "a2S43", + "name": "editorH2", + "fill": "$--foreground", + "content": "Architecture", + "lineHeight": 1.3, + "fontFamily": "Inter", + "fontSize": 24, + "fontWeight": "600" + }, + { + "type": "text", + "id": "wgvQa", + "name": "editorP2", + "fill": "$--foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "The app uses a four-panel layout: Sidebar for navigation, NoteList for browsing, Editor for content, and Inspector for metadata. All data lives in markdown files with YAML frontmatter, git-versioned.", + "lineHeight": 1.6, + "fontFamily": "Inter", + "fontSize": 16, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "GGP97", + "name": "Inspector", + "clip": true, + "width": 260, + "height": "fill_container", + "fill": "$--background", + "stroke": { + "align": "inside", + "thickness": { + "left": 1 + }, + "fill": "$--border" + }, + "layout": "vertical", + "children": [ + { + "type": "frame", + "id": "FbHy7", + "name": "Inspector Header", + "width": "fill_container", + "height": 45, + "stroke": { + "align": "inside", + "thickness": { + "bottom": 1 + }, + "fill": "$--border" + }, + "gap": 8, + "padding": [ + 0, + 12 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "sA7Dx", + "name": "leftGroup", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "0ccF1", + "name": "propIcon", + "width": 16, + "height": 16, + "iconFontName": "sliders-horizontal", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + }, + { + "type": "text", + "id": "T2vTZ", + "name": "inspTitle", + "fill": "$--muted-foreground", + "content": "Properties", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "600" + } + ] + }, + { + "type": "icon_font", + "id": "NQOhZ", + "name": "sidebarIcon", + "width": 16, + "height": 16, + "iconFontName": "x", + "iconFontFamily": "phosphor", + "fill": "$--muted-foreground" + } + ] + }, + { + "type": "frame", + "id": "LYFki", + "name": "Inspector Body", + "clip": true, + "width": "fill_container", + "height": "fill_container", + "layout": "vertical", + "gap": 16, + "padding": 12, + "children": [ + { + "type": "frame", + "id": "8g61y", + "name": "Properties Section", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "frame", + "id": "VhH4P", + "name": "addPropBtn", + "width": "fill_container", + "cornerRadius": 6, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + }, + "padding": [ + 6, + 12 + ], + "justifyContent": "center", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "Q9z8F", + "name": "addPropTxt", + "fill": "$--muted-foreground", + "content": "+ Add property", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "6PwTH", + "name": "propType", + "width": "fill_container", + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "Vt2BC", + "name": "propTypeLbl", + "fill": "$--muted-foreground", + "content": "TYPE", + "fontFamily": "IBM Plex Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "uvBiY", + "name": "propTypeVal", + "fill": "$--foreground", + "content": "Project", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "FDbay", + "name": "propStatus", + "width": "fill_container", + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "e426F", + "name": "propStatusLbl", + "fill": "$--muted-foreground", + "content": "STATUS", + "fontFamily": "IBM Plex Mono", + "fontSize": 10, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "o2rZM", + "name": "propStatusBadge", + "fill": "$--accent-green-light", + "cornerRadius": 16, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "transparent" + }, + "gap": 8, + "padding": [ + 1, + 6 + ], + "justifyContent": "center", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "QM7L3", + "name": "Badge Text", + "fill": "$--accent-green", + "content": "ACTIVE", + "lineHeight": 1.33, + "textAlign": "center", + "textAlignVertical": "middle", + "fontFamily": "IBM Plex Mono", + "fontSize": 10, + "fontWeight": "600", + "letterSpacing": 1.2 + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "NmRLv", + "name": "Relationships Section", + "width": "fill_container", + "layout": "vertical", + "gap": 16, + "children": [ + { + "type": "frame", + "id": "WFVAr", + "name": "relGroup1", + "width": "fill_container", + "layout": "vertical", + "gap": 4, + "children": [ + { + "type": "text", + "id": "0N729", + "name": "relGrp1Title", + "fill": "$--muted-foreground", + "content": "BELONGS TO", + "fontFamily": "IBM Plex Mono", + "fontSize": 10 + }, + { + "type": "frame", + "id": "zLl8F", + "name": "relLaputaBtn", + "width": "fill_container", + "fill": "$--accent-green-light", + "cornerRadius": 6, + "padding": [ + 6, + 10 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "kw9vC", + "name": "laputaTxt", + "fill": "$--accent-green", + "content": "Laputa", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + }, + { + "type": "icon_font", + "id": "RFvoz", + "name": "topicIcon", + "opacity": 0.5, + "width": 14, + "height": 14, + "iconFontName": "tag", + "iconFontFamily": "phosphor", + "fill": "$--accent-green" + } + ] + }, + { + "type": "frame", + "id": "ybi8Q", + "name": "relLaputaBtn", + "width": "fill_container", + "fill": "$--accent-green-light", + "cornerRadius": 6, + "padding": [ + 6, + 10 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "VNcmt", + "name": "laputaTxt", + "fill": "$--accent-green", + "content": "Building", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + }, + { + "type": "icon_font", + "id": "UVPwc", + "name": "topicIcon", + "opacity": 0.5, + "width": 14, + "height": 14, + "iconFontName": "tag", + "iconFontFamily": "phosphor", + "fill": "$--accent-green" + } + ] + }, + { + "type": "frame", + "id": "L6knW", + "name": "linkExisting", + "width": "fill_container", + "cornerRadius": 6, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + }, + "padding": [ + 6, + 10 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "jwU3Z", + "name": "laputaTxt", + "fill": "$--muted-foreground", + "content": "+ Link existing", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "frame", + "id": "laPFL", + "name": "relGroup2", + "width": "fill_container", + "layout": "vertical", + "gap": 4, + "children": [ + { + "type": "text", + "id": "LOXUL", + "name": "relGrp2Title", + "fill": "$--muted-foreground", + "content": "RELATED TO", + "fontFamily": "IBM Plex Mono", + "fontSize": 10 + }, + { + "type": "frame", + "id": "qLTYD", + "name": "relMigrationBtn", + "width": "fill_container", + "fill": "$--accent-red-light", + "cornerRadius": 6, + "padding": [ + 6, + 10 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "GBk2u", + "name": "migrationTxt", + "fill": "$--accent-red", + "content": "Shadcn Migration", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + }, + { + "type": "icon_font", + "id": "Sy14T", + "name": "expIcon", + "opacity": 0.5, + "width": 14, + "height": 14, + "iconFontName": "flask", + "iconFontFamily": "phosphor", + "fill": "$--accent-red" + } + ] + }, + { + "type": "frame", + "id": "u8ijV", + "name": "linkExisting", + "width": "fill_container", + "cornerRadius": 6, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + }, + "padding": [ + 6, + 10 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "PUXx5", + "name": "laputaTxt", + "fill": "$--muted-foreground", + "content": "+ Link existing", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "6PUnO", + "name": "Backlinks Section", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "frame", + "id": "PllIu", + "name": "blTitle", + "gap": 4, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "ZbgbI", + "name": "blTitleTxt", + "rotation": -0.5285936385085725, + "fill": "$--muted-foreground", + "content": "BACKLINKS", + "fontFamily": "IBM Plex Mono", + "fontSize": 10 + } + ] + }, + { + "type": "text", + "id": "eRoDO", + "name": "blItem1", + "fill": "$--primary", + "content": "Portfolio Rewrite", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "u1olR", + "name": "blItem2", + "fill": "$--primary", + "content": "Meeting with Grant", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "oUd9s", + "name": "blItem3", + "fill": "$--primary", + "content": "Q1 Planning", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "yf0wj", + "name": "History Section", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "text", + "id": "dG7rj", + "name": "histTitle", + "fill": "$--muted-foreground", + "content": "HISTORY", + "fontFamily": "IBM Plex Mono", + "fontSize": 10 + }, + { + "type": "frame", + "id": "z2Mdy", + "name": "histItem1", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "left": 2 + }, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 2, + "padding": [ + 0, + 0, + 0, + 10 + ], + "children": [ + { + "type": "text", + "id": "AsiWz", + "name": "histItem1Hash", + "fill": "$--accent-blue", + "content": "a089f44 · feat: migrate to shadcn/ui", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "BdVhi", + "name": "histItem1Date", + "fill": "$--muted-foreground", + "content": "Feb 16, 2026", + "fontFamily": "Inter", + "fontSize": 10, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "piJNC", + "name": "histItem2", + "width": "fill_container", + "stroke": { + "align": "inside", + "thickness": { + "left": 2 + }, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 2, + "padding": [ + 0, + 0, + 0, + 10 + ], + "children": [ + { + "type": "text", + "id": "0PWoX", + "name": "histItem2Hash", + "fill": "$--accent-blue", + "content": "5a4b4ac · feat: emoji favicon", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "xySNW", + "name": "histItem2Date", + "fill": "$--muted-foreground", + "content": "Feb 15, 2026", + "fontFamily": "Inter", + "fontSize": 10, + "fontWeight": "normal" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "x1AHT", + "name": "lightStatusBar", + "width": "fill_container", + "height": 30, + "fill": "$--sidebar", + "stroke": { + "align": "inside", + "thickness": { + "top": 1 + }, + "fill": "$--border" + }, + "padding": [ + 0, + 8 + ], + "justifyContent": "space_between", + "alignItems": "center", + "children": [ + { + "type": "frame", + "id": "e2UzJ", + "name": "Status Left", + "height": "fill_container", + "gap": 12, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "mlD58", + "name": "versionIcon", + "width": 14, + "height": 14, + "iconFontName": "box", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + }, + { + "type": "text", + "id": "hRBRa", + "name": "versionText", + "fill": "$--muted-foreground", + "content": "v0.4.2", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "500" + }, + { + "type": "text", + "id": "bFwrw", + "name": "sep1", + "fill": "$--border", + "content": "|", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "icon_font", + "id": "ey3Gr", + "name": "branchIcon", + "width": 14, + "height": 14, + "iconFontName": "git-branch", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + }, + { + "type": "text", + "id": "Kz0sS", + "name": "branchText", + "fill": "$--muted-foreground", + "content": "main", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "6IY1E", + "name": "sep2", + "fill": "$--border", + "content": "|", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "icon_font", + "id": "IjEG1", + "name": "syncIcon", + "width": 13, + "height": 13, + "iconFontName": "refresh-cw", + "iconFontFamily": "lucide", + "fill": "$--accent-green" + }, + { + "type": "text", + "id": "srxkr", + "name": "syncText", + "fill": "$--muted-foreground", + "content": "Synced 2m ago", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "4jgQF", + "name": "Status Right", + "height": "fill_container", + "gap": 12, + "alignItems": "center", + "children": [ + { + "type": "icon_font", + "id": "3RPmA", + "name": "aiIcon", + "width": 13, + "height": 13, + "iconFontName": "sparkles", + "iconFontFamily": "lucide", + "fill": "$--accent-purple" + }, + { + "type": "text", + "id": "t0NOA", + "name": "aiText", + "fill": "$--muted-foreground", + "content": "Claude Sonnet 4", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "16V7i", + "name": "sep3", + "fill": "$--border", + "content": "|", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "icon_font", + "id": "PXVrd", + "name": "notesCount", + "width": 13, + "height": 13, + "iconFontName": "file-text", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + }, + { + "type": "text", + "id": "2FxCi", + "name": "notesText", + "fill": "$--muted-foreground", + "content": "1,247 notes", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "pB6ds", + "name": "sep4", + "fill": "$--border", + "content": "|", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + }, + { + "type": "icon_font", + "id": "fRHKs", + "name": "bellIcon", + "width": 13, + "height": 13, + "iconFontName": "bell", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + }, + { + "type": "icon_font", + "id": "2sEBS", + "name": "settingsIcon", + "width": 13, + "height": 13, + "iconFontName": "settings", + "iconFontFamily": "lucide", + "fill": "$--muted-foreground" + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "mOf4J", + "x": 0, + "y": 4457, + "name": "Color Palette — shadcn/ui Theme (Light)", + "theme": { + "Mode": "Light" + }, + "width": 1440, + "fill": "$--background", + "layout": "vertical", + "gap": 32, + "padding": 40, + "children": [ + { + "type": "text", + "id": "rmJdn", + "name": "cTitle", + "fill": "$--foreground", + "content": "Color Palette — shadcn/ui Theme Variables (Light)", + "fontFamily": "Inter", + "fontSize": 28, + "fontWeight": "700", + "letterSpacing": -0.5 + }, + { + "type": "text", + "id": "V79Zu", + "name": "cSub", + "fill": "$--muted-foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "All colors from src/index.css. Variables support light/dark mode via .dark class.", + "lineHeight": 1.5, + "fontFamily": "Inter", + "fontSize": 14, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "Xbdzq", + "name": "primLbl", + "fill": "$--foreground", + "content": "Primary Colors", + "fontFamily": "Inter", + "fontSize": 18, + "fontWeight": "600" + }, + { + "type": "frame", + "id": "NyoC7", + "name": "primRow", + "width": "fill_container", + "gap": 16, + "children": [ + { + "type": "frame", + "id": "Inb1x", + "name": "sw1", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "HRsVC", + "name": "sw1b", + "fill": "$--background", + "width": "fill_container", + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + } + }, + { + "type": "text", + "id": "jV89s", + "name": "sw1n", + "fill": "$--foreground", + "content": "--background", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + }, + { + "type": "text", + "id": "qOUUY", + "name": "sw1v", + "fill": "$--muted-foreground", + "content": "L: #FFFFFF / D: #0f0f1a", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "QytAQ", + "name": "sw2", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "TMuHH", + "name": "sw2b", + "fill": "$--foreground", + "width": "fill_container", + "height": 60 + }, + { + "type": "text", + "id": "k9qK4", + "name": "sw2n", + "fill": "$--foreground", + "content": "--foreground", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + }, + { + "type": "text", + "id": "BfEn7", + "name": "sw2v", + "fill": "$--muted-foreground", + "content": "L: #37352F / D: #e0e0e0", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "gJZtB", + "name": "sw3", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "RRsQg", + "name": "sw3b", + "fill": "$--primary", + "width": "fill_container", + "height": 60 + }, + { + "type": "text", + "id": "CtJe4", + "name": "sw3n", + "fill": "$--foreground", + "content": "--primary", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + }, + { + "type": "text", + "id": "1iFr6", + "name": "sw3v", + "fill": "$--muted-foreground", + "content": "#155DFF", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "mbvua", + "name": "sw4", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "fajNZ", + "name": "sw4b", + "fill": "$--primary-foreground", + "width": "fill_container", + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + } + }, + { + "type": "text", + "id": "wVJ7z", + "name": "sw4n", + "fill": "$--foreground", + "content": "--primary-foreground", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + }, + { + "type": "text", + "id": "Oum5Q", + "name": "sw4v", + "fill": "$--muted-foreground", + "content": "L: #FFFFFF / D: #ffffff", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "text", + "id": "UvI4d", + "name": "accLbl", + "fill": "$--foreground", + "content": "Accent & Semantic Colors", + "fontFamily": "Inter", + "fontSize": 18, + "fontWeight": "600" + }, + { + "type": "frame", + "id": "jOmo7", + "name": "accRow", + "width": "fill_container", + "gap": 16, + "children": [ + { + "type": "frame", + "id": "uFXJ3", + "name": "a1", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "NRr0w", + "name": "a1b", + "fill": "$--accent-blue", + "width": "fill_container", + "height": 60 + }, + { + "type": "text", + "id": "ECVqk", + "name": "a1n", + "fill": "$--foreground", + "content": "--accent-blue", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "csKEt", + "name": "a2", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "5IiT2", + "name": "a2b", + "fill": "$--accent-green", + "width": "fill_container", + "height": 60 + }, + { + "type": "text", + "id": "aCPba", + "name": "a2n", + "fill": "$--foreground", + "content": "--accent-green", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "XbY7C", + "name": "a3", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "I9qCz", + "name": "a3b", + "fill": "$--accent-yellow", + "width": "fill_container", + "height": 60 + }, + { + "type": "text", + "id": "DfZVh", + "name": "a3n", + "fill": "$--foreground", + "content": "--accent-yellow", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "nROw3", + "name": "a4", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "IcIoP", + "name": "a4b", + "fill": "$--accent-red", + "width": "fill_container", + "height": 60 + }, + { + "type": "text", + "id": "eGgz6", + "name": "a4n", + "fill": "$--foreground", + "content": "--destructive", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "J95fv", + "name": "a5", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "o03fQ", + "name": "a5b", + "fill": "$--accent-purple", + "width": "fill_container", + "height": 60 + }, + { + "type": "text", + "id": "H9VWy", + "name": "a5n", + "fill": "$--foreground", + "content": "--accent-purple", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + } + ] + }, + { + "type": "text", + "id": "GcYle", + "name": "lightLightLbl", + "fill": "$--foreground", + "content": "Accent Light Backgrounds", + "fontFamily": "Inter", + "fontSize": 18, + "fontWeight": "600" + }, + { + "type": "frame", + "id": "Ju2xp", + "name": "accLightRow", + "width": "fill_container", + "gap": 16, + "children": [ + { + "type": "frame", + "id": "5dnn5", + "name": "al1", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "7kcqW", + "name": "ll1b", + "fill": "$--accent-blue-light", + "width": "fill_container", + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + } + }, + { + "type": "text", + "id": "dBCbF", + "name": "ll1n", + "fill": "$--foreground", + "content": "--accent-blue-light", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "W0t0j", + "name": "al2", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "1vepp", + "name": "ll2b", + "fill": "$--accent-green-light", + "width": "fill_container", + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + } + }, + { + "type": "text", + "id": "6kUyf", + "name": "ll2n", + "fill": "$--foreground", + "content": "--accent-green-light", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "zyV1j", + "name": "al3", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "yUDBN", + "name": "ll3b", + "fill": "$--accent-yellow-light", + "width": "fill_container", + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + } + }, + { + "type": "text", + "id": "dmfZ4", + "name": "ll3n", + "fill": "$--foreground", + "content": "--accent-yellow-light", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "yGmmo", + "name": "al4", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "eLu8o", + "name": "ll4b", + "fill": "$--accent-red-light", + "width": "fill_container", + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + } + }, + { + "type": "text", + "id": "PVLwR", + "name": "ll4n", + "fill": "$--foreground", + "content": "--accent-red-light", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "0BO2b", + "name": "al5", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "6FBws", + "name": "ll5b", + "fill": "$--accent-purple-light", + "width": "fill_container", + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + } + }, + { + "type": "text", + "id": "i2DfV", + "name": "ll5n", + "fill": "$--foreground", + "content": "--accent-purple-light", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + } + ] + }, + { + "type": "text", + "id": "JFocf", + "name": "surfLbl", + "fill": "$--foreground", + "content": "Surface & Border Colors", + "fontFamily": "Inter", + "fontSize": 18, + "fontWeight": "600" + }, + { + "type": "frame", + "id": "YAJLW", + "name": "surfRow", + "width": "fill_container", + "gap": 16, + "children": [ + { + "type": "frame", + "id": "JHS3j", + "name": "s1", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "9Bj41", + "name": "s1b", + "fill": "$--card", + "width": "fill_container", + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + } + }, + { + "type": "text", + "id": "wn4ua", + "name": "s1n", + "fill": "$--foreground", + "content": "--card", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "Fyvfd", + "name": "s2", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "UFLw5", + "name": "s2b", + "fill": "$--sidebar", + "width": "fill_container", + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--sidebar-border" + } + }, + { + "type": "text", + "id": "gngfL", + "name": "s2n", + "fill": "$--foreground", + "content": "--sidebar", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "zbyEJ", + "name": "s3", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "WnmQD", + "name": "s3b", + "fill": "$--secondary", + "width": "fill_container", + "height": 60 + }, + { + "type": "text", + "id": "wnkxR", + "name": "s3n", + "fill": "$--foreground", + "content": "--secondary", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "Z2FuK", + "name": "s4", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "lNPnc", + "name": "s4b", + "fill": "$--border", + "width": "fill_container", + "height": 60 + }, + { + "type": "text", + "id": "x9aeh", + "name": "s4n", + "fill": "$--foreground", + "content": "--border / --input", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "TDDQ0", + "name": "s5", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "MwHOe", + "name": "s5b", + "fill": "$--muted-foreground", + "width": "fill_container", + "height": 60 + }, + { + "type": "text", + "id": "dHZj5", + "name": "s5n", + "fill": "$--foreground", + "content": "--muted-foreground", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + } + ] + } + ] + }, + { + "type": "frame", + "id": "HZonq", + "x": 0, + "y": 5252, + "name": "Typography & Spacing Specs (Light)", + "theme": { + "Mode": "Light" + }, + "width": 1440, + "fill": "$--background", + "layout": "vertical", + "gap": 32, + "padding": 40, + "children": [ + { + "type": "text", + "id": "nGLlU", + "name": "tTitle", + "fill": "$--foreground", + "content": "Typography Scale (Light)", + "fontFamily": "Inter", + "fontSize": 28, + "fontWeight": "700", + "letterSpacing": -0.5 + }, + { + "type": "text", + "id": "gkQqO", + "name": "tSub", + "fill": "$--muted-foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Fonts: Inter, IBM Plex Mono. Base: 14px. System fallback: -apple-system, BlinkMacSystemFont, sans-serif.", + "lineHeight": 1.5, + "fontFamily": "Inter", + "fontSize": 14, + "fontWeight": "normal" + }, + { + "type": "frame", + "id": "QL9fK", + "name": "t1", + "width": "fill_container", + "gap": 24, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "px1l7", + "name": "t1s", + "fill": "$--foreground", + "content": "Heading 1", + "lineHeight": 1.2, + "fontFamily": "Inter", + "fontSize": 32, + "fontWeight": "700" + }, + { + "type": "text", + "id": "3UWKu", + "name": "t1d", + "fill": "$--muted-foreground", + "content": "32px / Bold / lh 1.2 — Editor H1", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "Yjhit", + "name": "t2", + "width": "fill_container", + "gap": 24, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "iFzl0", + "name": "t2s", + "fill": "$--foreground", + "content": "Heading 2", + "lineHeight": 1.3, + "fontFamily": "Inter", + "fontSize": 24, + "fontWeight": "600" + }, + { + "type": "text", + "id": "cF55I", + "name": "t2d", + "fill": "$--muted-foreground", + "content": "24px / Semibold / lh 1.3 — Editor H2", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "bBHMa", + "name": "t3", + "width": "fill_container", + "gap": 24, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "kpSyr", + "name": "t3s", + "fill": "$--foreground", + "content": "App Title", + "fontFamily": "Inter", + "fontSize": 17, + "fontWeight": "700", + "letterSpacing": -0.3 + }, + { + "type": "text", + "id": "xUuNz", + "name": "t3d", + "fill": "$--muted-foreground", + "content": "17px / Bold / ls -0.3 — Sidebar header", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "Lsh7F", + "name": "t4", + "width": "fill_container", + "gap": 24, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "vTRSs", + "name": "t4s", + "fill": "$--foreground", + "content": "Body Text", + "lineHeight": 1.6, + "fontFamily": "Inter", + "fontSize": 16, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "fKa3p", + "name": "t4d", + "fill": "$--muted-foreground", + "content": "16px / Regular / lh 1.6 — Editor paragraphs", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "rpOTz", + "name": "t5", + "width": "fill_container", + "gap": 24, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "VXY3Z", + "name": "t5s", + "fill": "$--foreground", + "content": "UI Label / Button", + "lineHeight": 1.43, + "fontFamily": "Inter", + "fontSize": 14, + "fontWeight": "500" + }, + { + "type": "text", + "id": "F6GQz", + "name": "t5d", + "fill": "$--muted-foreground", + "content": "14px / Medium / lh 1.43 — Buttons, NoteList header, Inspector labels", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "H6Rgt", + "name": "t6", + "width": "fill_container", + "gap": 24, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "xWvbj", + "name": "t6s", + "fill": "$--foreground", + "content": "Sidebar Item", + "fontFamily": "Inter", + "fontSize": 13, + "fontWeight": "500" + }, + { + "type": "text", + "id": "bvBBm", + "name": "t6d", + "fill": "$--muted-foreground", + "content": "13px / Medium — Sidebar nav items, filter items, search", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "GrFAq", + "name": "t7", + "width": "fill_container", + "gap": 24, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "vY8j4", + "name": "t7s", + "fill": "$--muted-foreground", + "content": "SECTION HEADER", + "fontFamily": "Inter", + "fontSize": 11, + "fontWeight": "600", + "letterSpacing": 0.5 + }, + { + "type": "text", + "id": "okws6", + "name": "t7d", + "fill": "$--muted-foreground", + "content": "11px / Semibold / ls 0.5 — Sidebar sections, type pills", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "awxls", + "name": "t8", + "width": "fill_container", + "gap": 24, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "mD3f5", + "name": "t8s", + "fill": "$--foreground", + "content": "ALL-CAPS LABEL", + "fontFamily": "IBM Plex Mono", + "fontSize": 11, + "fontWeight": "600", + "letterSpacing": 1.2 + }, + { + "type": "text", + "id": "jFpLw", + "name": "t8d", + "fill": "$--muted-foreground", + "content": "11px / Semibold / ls 1.2 / IBM Plex Mono — Section labels, metadata tags", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "ToLzL", + "name": "t9", + "width": "fill_container", + "gap": 24, + "alignItems": "center", + "children": [ + { + "type": "text", + "id": "LEsxW", + "name": "t9s", + "fill": "$--muted-foreground", + "content": "OVERLINE TEXT", + "fontFamily": "IBM Plex Mono", + "fontSize": 10, + "fontWeight": "500", + "letterSpacing": 1.5 + }, + { + "type": "text", + "id": "HQ3Df", + "name": "t9d", + "fill": "$--muted-foreground", + "content": "10px / Medium / ls 1.5 / IBM Plex Mono — Overlines, category labels, timestamps", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "rectangle", + "id": "3tiJ7", + "name": "div1", + "fill": "$--border", + "width": "fill_container", + "height": 1 + }, + { + "type": "text", + "id": "b2Mt9", + "name": "spTitle", + "fill": "$--foreground", + "content": "Spacing & Layout Reference", + "fontFamily": "Inter", + "fontSize": 28, + "fontWeight": "700", + "letterSpacing": -0.5 + }, + { + "type": "frame", + "id": "o6ETJ", + "name": "pnlRow", + "width": "fill_container", + "gap": 16, + "children": [ + { + "type": "frame", + "id": "HhbOn", + "name": "p1", + "width": "fill_container", + "cornerRadius": 8, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 6, + "padding": 16, + "children": [ + { + "type": "text", + "id": "YBekR", + "name": "p1t", + "fill": "$--foreground", + "content": "Sidebar: 250px", + "fontFamily": "Inter", + "fontSize": 14, + "fontWeight": "600" + }, + { + "type": "text", + "id": "dT8Xe", + "name": "p1d", + "fill": "$--muted-foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "bg: --sidebar | padding: 8px container, 12px 16px header, 6px 16px items", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "aYkMZ", + "name": "p2", + "width": "fill_container", + "cornerRadius": 8, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 6, + "padding": 16, + "children": [ + { + "type": "text", + "id": "Eycne", + "name": "p2t", + "fill": "$--foreground", + "content": "NoteList: 300px", + "fontFamily": "Inter", + "fontSize": 14, + "fontWeight": "600" + }, + { + "type": "text", + "id": "lyTZN", + "name": "p2d", + "fill": "$--muted-foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "bg: --card | padding: 14px 16px header, 8px 12px search/pills, 10px 16px items", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "E6G3g", + "name": "p3", + "width": "fill_container", + "cornerRadius": 8, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 6, + "padding": 16, + "children": [ + { + "type": "text", + "id": "Xy5Gl", + "name": "p3t", + "fill": "$--foreground", + "content": "Editor: flexible", + "fontFamily": "Inter", + "fontSize": 14, + "fontWeight": "600" + }, + { + "type": "text", + "id": "y4svr", + "name": "p3d", + "fill": "$--muted-foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "bg: --background | padding: 32px 64px content, 7px 14px tabs, gap: 16px", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "RBPav", + "name": "p4", + "width": "fill_container", + "cornerRadius": 8, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 6, + "padding": 16, + "children": [ + { + "type": "text", + "id": "xKUWC", + "name": "p4t", + "fill": "$--foreground", + "content": "Inspector: 280px", + "fontFamily": "Inter", + "fontSize": 14, + "fontWeight": "600" + }, + { + "type": "text", + "id": "u81W5", + "name": "p4d", + "fill": "$--muted-foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "bg: --sidebar | padding: 14px 12px header, 12px body, 16px section gap, 8px prop gap", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + } + ] + }, + { + "type": "text", + "id": "VvZyF", + "name": "radLbl", + "fill": "$--foreground", + "content": "Border Radius", + "fontFamily": "Inter", + "fontSize": 18, + "fontWeight": "600" + }, + { + "type": "frame", + "id": "OwJH7", + "name": "radRow", + "width": "fill_container", + "gap": 16, + "children": [ + { + "type": "frame", + "id": "DHrzV", + "name": "r1", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "rectangle", + "cornerRadius": 4, + "id": "V35oi", + "name": "r1b", + "fill": "$--secondary", + "width": 80, + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + } + }, + { + "type": "text", + "id": "HVQpq", + "name": "r1n", + "fill": "$--foreground", + "content": "4px (sm) — chips", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "wp350", + "name": "r2", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "rectangle", + "cornerRadius": 6, + "id": "E031z", + "name": "r2b", + "fill": "$--secondary", + "width": 80, + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + } + }, + { + "type": "text", + "id": "rDLff", + "name": "r2n", + "fill": "$--foreground", + "content": "6px (md) — buttons, inputs", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "d6tJt", + "name": "r3", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "rectangle", + "cornerRadius": 8, + "id": "PNkS4", + "name": "r3b", + "fill": "$--secondary", + "width": 80, + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + } + }, + { + "type": "text", + "id": "rZGXb", + "name": "r3n", + "fill": "$--foreground", + "content": "8px (lg) — cards, dialogs", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + }, + { + "type": "frame", + "id": "UAZ8u", + "name": "r4", + "width": "fill_container", + "layout": "vertical", + "gap": 8, + "alignItems": "center", + "children": [ + { + "type": "rectangle", + "cornerRadius": 16, + "id": "wgMVG", + "name": "r4b", + "fill": "$--secondary", + "width": 80, + "height": 60, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + } + }, + { + "type": "text", + "id": "zw3RY", + "name": "r4n", + "fill": "$--foreground", + "content": "16px — badges", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "500" + } + ] + } + ] + }, + { + "type": "rectangle", + "id": "kIrXE", + "name": "div2", + "fill": "$--border", + "width": "fill_container", + "height": 1 + }, + { + "type": "text", + "id": "FYw8k", + "name": "compTitle", + "fill": "$--foreground", + "content": "shadcn/ui Component Inventory", + "fontFamily": "Inter", + "fontSize": 28, + "fontWeight": "700", + "letterSpacing": -0.5 + }, + { + "type": "frame", + "id": "7Q1Fi", + "name": "compRow", + "width": "fill_container", + "gap": 16, + "children": [ + { + "type": "frame", + "id": "XfUwj", + "name": "cg1", + "width": "fill_container", + "fill": "$--card", + "cornerRadius": 8, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 12, + "padding": 16, + "children": [ + { + "type": "text", + "id": "ly7m0", + "name": "cg1t", + "fill": "$--accent-green", + "content": "In Use", + "fontFamily": "Inter", + "fontSize": 14, + "fontWeight": "600" + }, + { + "type": "text", + "id": "Re89a", + "name": "cg1r1", + "fill": "$--foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Button — 5 variants (Default, Secondary, Outline, Ghost, Destructive)", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "ILbjW", + "name": "cg1r2", + "fill": "$--foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Input — Default text input with label group", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "eurqS", + "name": "cg1r3", + "fill": "$--foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Dialog — Modal overlay (CreateNote, Commit)", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "AZyZP", + "name": "cg1r4", + "fill": "$--foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Badge — 4 variants (Default, Secondary, Outline, Destructive)", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + }, + { + "type": "frame", + "id": "WtehQ", + "name": "cg2", + "width": "fill_container", + "fill": "$--card", + "cornerRadius": 8, + "stroke": { + "align": "inside", + "thickness": 1, + "fill": "$--border" + }, + "layout": "vertical", + "gap": 12, + "padding": 16, + "children": [ + { + "type": "text", + "id": "KaMZ9", + "name": "cg2t", + "fill": "$--accent-yellow", + "content": "Available (Not Yet Used)", + "fontFamily": "Inter", + "fontSize": 14, + "fontWeight": "600" + }, + { + "type": "text", + "id": "F8wam", + "name": "cg2r1", + "fill": "$--foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "DropdownMenu — Context / dropdown menus", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "Nl1aV", + "name": "cg2r2", + "fill": "$--foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Tabs — Tab navigation component", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + }, + { + "type": "text", + "id": "0sBwH", + "name": "cg2r3", + "fill": "$--foreground", + "textGrowth": "fixed-width", + "width": "fill_container", + "content": "Tooltip, Select, Card, Separator, ScrollArea", + "fontFamily": "Inter", + "fontSize": 12, + "fontWeight": "normal" + } + ] + } + ] + } + ] + } + ], + "themes": { + "Mode": [ + "Dark" + ] + }, + "variables": { + "--accent": { + "type": "color", + "value": [ + { + "value": "#EBEBEA" + }, + { + "value": "#252545", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--accent-blue": { + "type": "color", + "value": [ + { + "value": "#2383E2" + }, + { + "value": "#4a9eff", + "theme": { + "Mode": "Dark" + } + }, + { + "value": "#155DFF" + }, + { + "value": "#155DFF", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--accent-foreground": { + "type": "color", + "value": [ + { + "value": "#37352F" + }, + { + "value": "#ffffff", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--accent-green": { + "type": "color", + "value": [ + { + "value": "#0F7B6C" + }, + { + "value": "#4caf50", + "theme": { + "Mode": "Dark" + } + }, + { + "value": "#00B38B" + }, + { + "value": "#00B38B", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--accent-orange": { + "type": "color", + "value": [ + { + "value": "#D9730D" + }, + { + "value": "#ff9800", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--accent-purple": { + "type": "color", + "value": [ + { + "value": "#9065B0" + }, + { + "value": "#9c72ff", + "theme": { + "Mode": "Dark" + } + }, + { + "value": "#A932FF" + }, + { + "value": "#A932FF", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--accent-red": { + "type": "color", + "value": [ + { + "value": "#E03E3E" + }, + { + "value": "#f44336", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--background": { + "type": "color", + "value": [ + { + "value": "#FFFFFF" + }, + { + "value": "#0f0f1a", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--black": { + "type": "color", + "value": "#000000" + }, + "--border": { + "type": "color", + "value": [ + { + "value": "#E9E9E7" + }, + { + "value": "#2a2a4a", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--card": { + "type": "color", + "value": [ + { + "value": "#FFFFFF" + }, + { + "value": "#16162a", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--card-foreground": { + "type": "color", + "value": [ + { + "value": "#37352F" + }, + { + "value": "#e0e0e0", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--destructive": { + "type": "color", + "value": [ + { + "value": "#E03E3E" + }, + { + "value": "#f44336", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--destructive-foreground": { + "type": "color", + "value": [ + { + "value": "#FFFFFF" + }, + { + "value": "#ffffff", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--font-primary": { + "type": "string", + "value": [ + { + "value": "Inter" + }, + { + "value": "-apple-system, BlinkMacSystemFont, Inter, sans-serif" + }, + { + "value": "Inter" + } + ] + }, + "--font-system": { + "type": "string", + "value": "-apple-system, BlinkMacSystemFont, sans-serif" + }, + "--foreground": { + "type": "color", + "value": [ + { + "value": "#37352F" + }, + { + "value": "#e0e0e0", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--input": { + "type": "color", + "value": [ + { + "value": "#E9E9E7" + }, + { + "value": "#2a2a4a", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--muted": { + "type": "color", + "value": [ + { + "value": "#F0F0EF" + }, + { + "value": "#1e1e3a", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--muted-foreground": { + "type": "color", + "value": [ + { + "value": "#787774" + }, + { + "value": "#888888", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--popover": { + "type": "color", + "value": [ + { + "value": "#FFFFFF" + }, + { + "value": "#1e1e3a", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--popover-foreground": { + "type": "color", + "value": [ + { + "value": "#37352F" + }, + { + "value": "#e0e0e0", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--primary": { + "type": "color", + "value": [ + { + "value": "#2383E2" + }, + { + "value": "#4a9eff", + "theme": { + "Mode": "Dark" + } + }, + { + "value": "#155DFF" + }, + { + "value": "#155DFF", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--primary-foreground": { + "type": "color", + "value": [ + { + "value": "#FFFFFF" + }, + { + "value": "#ffffff", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--radius-lg": { + "type": "number", + "value": 8 + }, + "--radius-md": { + "type": "number", + "value": 6 + }, + "--radius-sm": { + "type": "number", + "value": 4 + }, + "--ring": { + "type": "color", + "value": [ + { + "value": "#2383E2" + }, + { + "value": "#4a9eff", + "theme": { + "Mode": "Dark" + } + }, + { + "value": "#155DFF" + }, + { + "value": "#155DFF", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--secondary": { + "type": "color", + "value": [ + { + "value": "#EBEBEA" + }, + { + "value": "#2a2a4a", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--secondary-foreground": { + "type": "color", + "value": [ + { + "value": "#37352F" + }, + { + "value": "#e0e0e0", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--sidebar": { + "type": "color", + "value": [ + { + "value": "#F7F6F3" + }, + { + "value": "#1a1a2e", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--sidebar-accent": { + "type": "color", + "value": [ + { + "value": "#EBEBEA" + }, + { + "value": "#252545", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--sidebar-accent-foreground": { + "type": "color", + "value": [ + { + "value": "#37352F" + }, + { + "value": "#ffffff", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--sidebar-border": { + "type": "color", + "value": [ + { + "value": "#E9E9E7" + }, + { + "value": "#2a2a4a", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--sidebar-foreground": { + "type": "color", + "value": [ + { + "value": "#37352F" + }, + { + "value": "#e0e0e0", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--sidebar-primary": { + "type": "color", + "value": [ + { + "value": "#2383E2" + }, + { + "value": "#4a9eff", + "theme": { + "Mode": "Dark" + } + }, + { + "value": "#155DFF" + }, + { + "value": "#155DFF", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--sidebar-primary-foreground": { + "type": "color", + "value": [ + { + "value": "#FFFFFF" + }, + { + "value": "#ffffff", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--sidebar-ring": { + "type": "color", + "value": [ + { + "value": "#2383E2" + }, + { + "value": "#4a9eff", + "theme": { + "Mode": "Dark" + } + }, + { + "value": "#155DFF" + }, + { + "value": "#155DFF", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--white": { + "type": "color", + "value": "#ffffff" + }, + "--accent-yellow": { + "type": "color", + "value": [ + { + "value": "#F0B100" + }, + { + "value": "#F0B100", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--accent-blue-light": { + "type": "color", + "value": [ + { + "value": "#155DFF14" + }, + { + "value": "#155DFF20", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--accent-green-light": { + "type": "color", + "value": [ + { + "value": "#00B38B14" + }, + { + "value": "#00B38B20", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--accent-purple-light": { + "type": "color", + "value": [ + { + "value": "#A932FF14" + }, + { + "value": "#A932FF20", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--accent-red-light": { + "type": "color", + "value": [ + { + "value": "#E03E3E14" + }, + { + "value": "#f4433620", + "theme": { + "Mode": "Dark" + } + } + ] + }, + "--accent-yellow-light": { + "type": "color", + "value": [ + { + "value": "#F0B10014" + }, + { + "value": "#F0B10020", + "theme": { + "Mode": "Dark" + } + } + ] + } + }, + "fonts": [ + { + "name": "GT Pressura Trial", + "url": "GT-Pressura-Regular-Trial.otf" + } + ] +} \ No newline at end of file From f36cfd1808fcbf18bdc8664e1014595ae3c8f636 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Sat, 21 Feb 2026 13:23:10 +0100 Subject: [PATCH 2/4] feat: add icon and color fields to VaultEntry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add icon (string|null) and color (string|null) to VaultEntry interface - Update Rust VaultEntry struct and frontmatter parsing to read icon/color - Add icon/color to SKIP_KEYS so they don't appear as relationships - Set mock data: Recipe type → cooking-pot/orange, Book type → book-open/green - Update all test files with the new required fields Co-Authored-By: Claude Opus 4.6 --- src-tauri/src/vault.rs | 11 ++++++ src/components/Editor.test.tsx | 2 ++ src/components/Inspector.test.tsx | 4 +++ src/components/NoteList.test.tsx | 10 ++++++ src/components/Sidebar.test.tsx | 24 +++++++++++++ src/hooks/useNoteActions.ts | 4 +-- src/mock-tauri.ts | 60 +++++++++++++++++++++++++++++++ src/types.ts | 4 +++ 8 files changed, 117 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/vault.rs b/src-tauri/src/vault.rs index 14f6e479..930dd0c6 100644 --- a/src-tauri/src/vault.rs +++ b/src-tauri/src/vault.rs @@ -34,6 +34,10 @@ pub struct VaultEntry { /// Generic relationship fields: any frontmatter key whose value contains wikilinks. /// Key is the original frontmatter field name (e.g. "Has", "Topics", "Events"). pub relationships: HashMap>, + /// Phosphor icon name (kebab-case) for Type entries, e.g. "cooking-pot". + pub icon: Option, + /// Accent color key for Type entries: "red", "purple", "blue", "green", "yellow", "orange". + pub color: Option, } /// Intermediate struct to capture YAML frontmatter fields. @@ -57,6 +61,10 @@ struct Frontmatter { created_at: Option, #[serde(rename = "Created time")] created_time: Option, + #[serde(default)] + icon: Option, + #[serde(default)] + color: Option, } /// Handles YAML fields that can be either a single string or a list of strings. @@ -203,6 +211,7 @@ fn parse_frontmatter(data: &HashMap) -> Frontmatter { /// Only skip keys that can never contain wikilinks. const SKIP_KEYS: &[&str] = &[ "is a", "aliases", "status", "cadence", "created at", "created time", + "icon", "color", ]; /// Check if a string contains a wikilink pattern `[[...]]`. @@ -352,6 +361,8 @@ pub fn parse_md_file(path: &Path) -> Result { owner: frontmatter.owner, cadence: frontmatter.cadence, modified_at, created_at, file_size, + icon: frontmatter.icon, + color: frontmatter.color, }) } diff --git a/src/components/Editor.test.tsx b/src/components/Editor.test.tsx index 313f523d..d65b4460 100644 --- a/src/components/Editor.test.tsx +++ b/src/components/Editor.test.tsx @@ -49,6 +49,8 @@ const mockEntry: VaultEntry = { fileSize: 1024, snippet: '', relationships: {}, + icon: null, + color: null, } const mockContent = `--- diff --git a/src/components/Inspector.test.tsx b/src/components/Inspector.test.tsx index d3a1cc63..555ab734 100644 --- a/src/components/Inspector.test.tsx +++ b/src/components/Inspector.test.tsx @@ -19,6 +19,8 @@ const mockEntry: VaultEntry = { fileSize: 1024, snippet: '', relationships: {}, + icon: null, + color: null, } const mockContent = `--- @@ -54,6 +56,8 @@ const referrerEntry: VaultEntry = { fileSize: 200, snippet: '', relationships: {}, + icon: null, + color: null, } const allContent: Record = { diff --git a/src/components/NoteList.test.tsx b/src/components/NoteList.test.tsx index b07d369f..fc6834a1 100644 --- a/src/components/NoteList.test.tsx +++ b/src/components/NoteList.test.tsx @@ -25,6 +25,8 @@ const mockEntries: VaultEntry[] = [ relationships: { 'Related to': ['[[topic/software-development]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/note/facebook-ads-strategy.md', @@ -45,6 +47,8 @@ const mockEntries: VaultEntry[] = [ 'Belongs to': ['[[project/26q1-laputa-app]]'], 'Related to': ['[[topic/growth]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/person/matteo-cellini.md', @@ -62,6 +66,8 @@ const mockEntries: VaultEntry[] = [ fileSize: 320, snippet: 'Sponsorship manager.', relationships: {}, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/event/2026-02-14-kickoff.md', @@ -79,6 +85,8 @@ const mockEntries: VaultEntry[] = [ fileSize: 512, snippet: 'Project kickoff meeting notes.', relationships: {}, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/topic/software-development.md', @@ -96,6 +104,8 @@ const mockEntries: VaultEntry[] = [ fileSize: 256, snippet: 'Frontend, backend, and systems programming.', relationships: {}, + icon: null, + color: null, }, ] diff --git a/src/components/Sidebar.test.tsx b/src/components/Sidebar.test.tsx index 281a63fb..8a0ac073 100644 --- a/src/components/Sidebar.test.tsx +++ b/src/components/Sidebar.test.tsx @@ -20,6 +20,8 @@ const mockEntries: VaultEntry[] = [ fileSize: 1024, snippet: '', relationships: {}, + icon: null, + color: null, }, { path: '/vault/responsibility/grow-newsletter.md', @@ -37,6 +39,8 @@ const mockEntries: VaultEntry[] = [ fileSize: 512, snippet: '', relationships: {}, + icon: null, + color: null, }, { path: '/vault/experiment/stock-screener.md', @@ -54,6 +58,8 @@ const mockEntries: VaultEntry[] = [ fileSize: 256, snippet: '', relationships: {}, + icon: null, + color: null, }, { path: '/vault/procedure/weekly-essays.md', @@ -71,6 +77,8 @@ const mockEntries: VaultEntry[] = [ fileSize: 128, snippet: '', relationships: {}, + icon: null, + color: null, }, { path: '/vault/topic/software-development.md', @@ -88,6 +96,8 @@ const mockEntries: VaultEntry[] = [ fileSize: 256, snippet: '', relationships: {}, + icon: null, + color: null, }, { path: '/vault/topic/trading.md', @@ -105,6 +115,8 @@ const mockEntries: VaultEntry[] = [ fileSize: 180, snippet: '', relationships: {}, + icon: null, + color: null, }, { path: '/vault/person/alice.md', @@ -122,6 +134,8 @@ const mockEntries: VaultEntry[] = [ fileSize: 100, snippet: '', relationships: {}, + icon: null, + color: null, }, { path: '/vault/event/kickoff.md', @@ -139,6 +153,8 @@ const mockEntries: VaultEntry[] = [ fileSize: 200, snippet: '', relationships: {}, + icon: null, + color: null, }, ] @@ -283,6 +299,8 @@ describe('Sidebar', () => { fileSize: 200, snippet: '', relationships: {}, + icon: null, + color: null, }, { path: '/vault/type/book.md', @@ -300,6 +318,8 @@ describe('Sidebar', () => { fileSize: 200, snippet: '', relationships: {}, + icon: null, + color: null, }, { path: '/vault/recipe/pasta.md', @@ -317,6 +337,8 @@ describe('Sidebar', () => { fileSize: 300, snippet: '', relationships: {}, + icon: null, + color: null, }, ] @@ -362,6 +384,8 @@ describe('Sidebar', () => { fileSize: 200, snippet: '', relationships: {}, + icon: null, + color: null, } render( {}} />) // "Projects" should appear once (the built-in section), not twice diff --git a/src/hooks/useNoteActions.ts b/src/hooks/useNoteActions.ts index ad9bd0eb..f844d02c 100644 --- a/src/hooks/useNoteActions.ts +++ b/src/hooks/useNoteActions.ts @@ -214,7 +214,7 @@ export function useNoteActions( aliases: [], belongsTo: [], relatedTo: [], status: noStatusTypes.has(type) ? null : 'Active', owner: null, cadence: null, modifiedAt: now, createdAt: now, fileSize: 0, - snippet: '', relationships: {}, + snippet: '', relationships: {}, icon: null, color: null, } const frontmatter = [ @@ -242,7 +242,7 @@ export function useNoteActions( aliases: [], belongsTo: [], relatedTo: [], status: null, owner: null, cadence: null, modifiedAt: now, createdAt: now, fileSize: 0, - snippet: '', relationships: {}, + snippet: '', relationships: {}, icon: null, color: null, } const content = `---\nIs A: Type\n---\n\n# ${typeName}\n\n` diff --git a/src/mock-tauri.ts b/src/mock-tauri.ts index e6f07b44..1ee86e49 100644 --- a/src/mock-tauri.ts +++ b/src/mock-tauri.ts @@ -552,6 +552,8 @@ A **general-purpose document** — research notes, meeting notes, strategy docs. // --- Custom type documents (user-created types) --- '/Users/luca/Laputa/type/recipe.md': `--- Is A: Type +icon: cooking-pot +color: orange --- # Recipe @@ -565,6 +567,8 @@ A **recipe** for cooking or baking. Recipes have ingredients, steps, and serving `, '/Users/luca/Laputa/type/book.md': `--- Is A: Type +icon: book-open +color: green --- # Book @@ -631,6 +635,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Related to': ['[[topic/software-development]]'], 'Type': ['[[type/project]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/responsibility/grow-newsletter.md', @@ -657,6 +663,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Related to': ['[[topic/growth]]'], 'Type': ['[[type/responsibility]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/responsibility/manage-sponsorships.md', @@ -677,6 +685,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Owner': ['[[person/matteo-cellini|Matteo Cellini]]'], 'Type': ['[[type/responsibility]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/procedure/write-weekly-essays.md', @@ -697,6 +707,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Belongs to': ['[[responsibility/grow-newsletter]]'], 'Type': ['[[type/procedure]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/procedure/run-sponsorships.md', @@ -717,6 +729,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Belongs to': ['[[responsibility/manage-sponsorships]]'], 'Type': ['[[type/procedure]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/experiment/stock-screener.md', @@ -738,6 +752,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Has Data': ['[[data/ema200-backtest-results]]'], 'Type': ['[[type/experiment]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/note/facebook-ads-strategy.md', @@ -759,6 +775,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Related to': ['[[topic/growth]]', '[[topic/ads]]'], 'Type': ['[[type/note]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/note/budget-allocation.md', @@ -779,6 +797,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Belongs to': ['[[project/26q1-laputa-app]]'], 'Type': ['[[type/note]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/person/matteo-cellini.md', @@ -798,6 +818,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: { 'Type': ['[[type/person]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/event/2026-02-14-laputa-app-kickoff.md', @@ -818,6 +840,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Related to': ['[[project/26q1-laputa-app]]', '[[person/matteo-cellini]]'], 'Type': ['[[type/event]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/topic/software-development.md', @@ -838,6 +862,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Notes': ['[[note/facebook-ads-strategy]]', '[[note/budget-allocation]]'], 'Type': ['[[type/topic]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/topic/trading.md', @@ -858,6 +884,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Notes': ['[[experiment/stock-screener]]'], 'Type': ['[[type/topic]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/essay/on-writing-well.md', @@ -878,6 +906,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Belongs to': ['[[responsibility/grow-newsletter]]'], 'Type': ['[[type/essay]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/essay/engineering-leadership-101.md', @@ -899,6 +929,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Related to': ['[[topic/software-development]]'], 'Type': ['[[type/essay]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/essay/ai-agents-primer.md', @@ -919,6 +951,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ 'Belongs to': ['[[responsibility/grow-newsletter]]'], 'Type': ['[[type/essay]]'], }, + icon: null, + color: null, }, // --- Type documents --- { @@ -937,6 +971,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ fileSize: 320, snippet: 'A time-bound initiative that advances a Responsibility. Projects have a clear start, end, and deliverables.', relationships: {}, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/type/responsibility.md', @@ -954,6 +990,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ fileSize: 280, snippet: 'An ongoing area of ownership — something you\'re accountable for indefinitely.', relationships: {}, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/type/procedure.md', @@ -971,6 +1009,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ fileSize: 310, snippet: 'A recurring process tied to a Responsibility. Procedures have a cadence and describe how to do something.', relationships: {}, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/type/experiment.md', @@ -988,6 +1028,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ fileSize: 290, snippet: 'A hypothesis-driven investigation with a clear test and measurable outcome.', relationships: {}, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/type/person.md', @@ -1005,6 +1047,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ fileSize: 200, snippet: 'A person you interact with — team members, collaborators, contacts.', relationships: {}, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/type/event.md', @@ -1022,6 +1066,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ fileSize: 180, snippet: 'A point-in-time occurrence — meetings, launches, milestones.', relationships: {}, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/type/topic.md', @@ -1039,6 +1085,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ fileSize: 170, snippet: 'A subject area for categorization. Topics group related notes, projects, and resources by theme.', relationships: {}, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/type/essay.md', @@ -1056,6 +1104,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ fileSize: 200, snippet: 'A published piece of writing — newsletter essays, blog posts, articles.', relationships: {}, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/type/note.md', @@ -1073,6 +1123,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ fileSize: 190, snippet: 'A general-purpose document — research notes, meeting notes, strategy docs.', relationships: {}, + icon: null, + color: null, }, // --- Custom type documents (user-created types) --- { @@ -1091,6 +1143,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ fileSize: 250, snippet: 'A recipe for cooking or baking. Recipes have ingredients, steps, and serving info.', relationships: {}, + icon: 'cooking-pot', + color: 'orange', }, { path: '/Users/luca/Laputa/type/book.md', @@ -1108,6 +1162,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ fileSize: 220, snippet: 'A book you\'re reading or have read. Track reading progress, notes, and key takeaways.', relationships: {}, + icon: 'book-open', + color: 'green', }, // --- Instances of custom types --- { @@ -1128,6 +1184,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: { 'Type': ['[[type/recipe]]'], }, + icon: null, + color: null, }, { path: '/Users/luca/Laputa/book/designing-data-intensive-applications.md', @@ -1147,6 +1205,8 @@ const MOCK_ENTRIES: VaultEntry[] = [ relationships: { 'Type': ['[[type/book]]'], }, + icon: null, + color: null, }, ] diff --git a/src/types.ts b/src/types.ts index 0f15d9eb..2a973fb0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -15,6 +15,10 @@ export interface VaultEntry { snippet: string /** Generic relationship fields: any frontmatter key whose value contains wikilinks. */ relationships: Record + /** Phosphor icon name (kebab-case) for Type entries, e.g. "cooking-pot" */ + icon: string | null + /** Accent color key for Type entries: "red" | "purple" | "blue" | "green" | "yellow" | "orange" */ + color: string | null } export interface GitCommit { From b1e15dd5ea994717b70114f6a90f29228c78c12f Mon Sep 17 00:00:00 2001 From: lucaronin Date: Sat, 21 Feb 2026 13:27:33 +0100 Subject: [PATCH 3/4] feat: implement icon & color customization for types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add TypeCustomizePopover component with color swatches (6 accent colors) and icon grid (35 curated Phosphor icons) - Add right-click context menu on sidebar section group headers with "Customize icon & color…" option - Sidebar renders custom icons/colors from type entry frontmatter - NoteList uses custom icons/colors for type badges and pinned cards - Wire up onCustomizeType in App.tsx to update type frontmatter - Add updateEntry to useVaultLoader for instant in-memory updates - Add --accent-orange-light CSS variable - typeColors utility now accepts optional custom color key overrides Product decision: Built-in types can also be customized (override their defaults), not just custom types. The picker is a simple popover that appears after right-click → "Customize icon & color…". Co-Authored-By: Claude Opus 4.6 --- src/App.tsx | 12 +- src/components/NoteList.tsx | 38 +++++-- src/components/Sidebar.tsx | 145 ++++++++++++++++++++++-- src/components/TypeCustomizePopover.tsx | 145 ++++++++++++++++++++++++ src/hooks/useVaultLoader.ts | 5 + src/index.css | 1 + src/utils/typeColors.ts | 25 +++- 7 files changed, 345 insertions(+), 26 deletions(-) create mode 100644 src/components/TypeCustomizePopover.tsx diff --git a/src/App.tsx b/src/App.tsx index 882c9cf1..10265e39 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -102,6 +102,16 @@ function App() { setToastMessage(`Type "${name}" created`) }, [notes, setToastMessage]) + const handleCustomizeType = useCallback((typeName: string, icon: string, color: string) => { + const typeEntry = vault.entries.find((e) => e.isA === 'Type' && e.title === typeName) + if (!typeEntry) return + // Update icon and color in frontmatter (two separate calls) + notes.handleUpdateFrontmatter(typeEntry.path, 'icon', icon) + notes.handleUpdateFrontmatter(typeEntry.path, 'color', color) + // Also update the entry in-memory for instant UI feedback + vault.updateEntry(typeEntry.path, { icon, color }) + }, [vault, notes]) + useAppKeyboard({ onQuickOpen: () => setShowQuickOpen(true), onCreateNote: openCreateDialog, @@ -151,7 +161,7 @@ function App() {
- setShowCommitDialog(true)} /> + setShowCommitDialog(true)} />
diff --git a/src/components/NoteList.tsx b/src/components/NoteList.tsx index 47e870ba..71028944 100644 --- a/src/components/NoteList.tsx +++ b/src/components/NoteList.tsx @@ -9,6 +9,7 @@ import { } from '@phosphor-icons/react' import type { ComponentType, SVGAttributes } from 'react' import { getTypeColor, getTypeLightColor } from '../utils/typeColors' +import { resolveIcon } from './TypeCustomizePopover' const TYPE_ICON_MAP: Record>> = { Project: Wrench, @@ -21,7 +22,8 @@ const TYPE_ICON_MAP: Record>> Type: StackSimple, } -function getTypeIcon(isA: string | null): ComponentType> { +function getTypeIcon(isA: string | null, customIcon?: string | null): ComponentType> { + if (customIcon) return resolveIcon(customIcon) return (isA && TYPE_ICON_MAP[isA]) || FileText } @@ -234,6 +236,15 @@ function NoteListInner({ entries, selection, selectedNote, allContent, modifiedF }) }, []) + // Build type entry map for custom icon/color lookup + const typeEntryMap = useMemo(() => { + const map: Record = {} + for (const e of entries) { + if (e.isA === 'Type') map[e.title] = e + } + return map + }, [entries]) + // Find the type document for this section group (e.g., type/project.md for "Project") const typeDocument = useMemo(() => { if (!isSectionGroup) return null @@ -278,9 +289,10 @@ function NoteListInner({ entries, selection, selectedNote, allContent, modifiedF const renderItem = useCallback((entry: VaultEntry, isPinned = false) => { const isSelected = selectedNote?.path === entry.path && !isPinned - const typeColor = getTypeColor(entry.isA ?? 'Note') - const typeLightColor = getTypeLightColor(entry.isA ?? 'Note') - const TypeIcon = getTypeIcon(entry.isA) + const te = typeEntryMap[entry.isA ?? ''] + const typeColor = getTypeColor(entry.isA ?? 'Note', te?.color) + const typeLightColor = getTypeLightColor(entry.isA ?? 'Note', te?.color) + const TypeIcon = getTypeIcon(entry.isA, te?.icon) return (
) - }, [selectedNote?.path, onSelectNote]) + }, [selectedNote?.path, onSelectNote, typeEntryMap]) const renderPinnedView = useCallback((entity: VaultEntry, groups: RelationshipGroup[]) => { - const entityTypeColor = getTypeColor(entity.isA ?? '') - const entityLightColor = getTypeLightColor(entity.isA ?? '') - const EntityIcon = getTypeIcon(entity.isA) + const ete = typeEntryMap[entity.isA ?? ''] + const entityTypeColor = getTypeColor(entity.isA ?? '', ete?.color) + const entityLightColor = getTypeLightColor(entity.isA ?? '', ete?.color) + const EntityIcon = getTypeIcon(entity.isA, ete?.icon) return (
{/* Prominent card */} @@ -387,7 +400,7 @@ function NoteListInner({ entries, selection, selectedNote, allContent, modifiedF )}
) - }, [onSelectNote, query, collapsedGroups, toggleGroup, renderItem]) + }, [onSelectNote, query, collapsedGroups, toggleGroup, renderItem, typeEntryMap]) return (
@@ -436,9 +449,10 @@ function NoteListInner({ entries, selection, selectedNote, allContent, modifiedF
{/* Type document pinned card (for sectionGroup view) */} {typeDocument && (() => { - const tdColor = getTypeColor(typeDocument.isA ?? 'Type') - const tdLightColor = getTypeLightColor(typeDocument.isA ?? 'Type') - const TDIcon = getTypeIcon(typeDocument.isA) + const tde = typeEntryMap[typeDocument.title] ?? typeDocument + const tdColor = getTypeColor(typeDocument.isA ?? 'Type', tde?.color) + const tdLightColor = getTypeLightColor(typeDocument.isA ?? 'Type', tde?.color) + const TDIcon = getTypeIcon(typeDocument.isA, tde?.icon) return (
void onCreateType?: (type: string) => void onCreateNewType?: () => void + onCustomizeType?: (typeName: string, icon: string, color: string) => void modifiedCount?: number onCommitPush?: () => void } @@ -39,6 +41,7 @@ interface SectionGroup { label: string type: string Icon: ComponentType + customColor?: string | null } const BUILT_IN_SECTION_GROUPS: SectionGroup[] = [ @@ -54,13 +57,31 @@ const BUILT_IN_SECTION_GROUPS: SectionGroup[] = [ const BUILT_IN_TYPES = new Set(BUILT_IN_SECTION_GROUPS.map((s) => s.type)) -export const Sidebar = memo(function Sidebar({ entries, selection, onSelect, onSelectNote, onCreateType, onCreateNewType, modifiedCount = 0, onCommitPush }: SidebarProps) { +export const Sidebar = memo(function Sidebar({ entries, selection, onSelect, onSelectNote, onCreateType, onCreateNewType, onCustomizeType, modifiedCount = 0, onCommitPush }: SidebarProps) { const [collapsed, setCollapsed] = useState>({}) + const [customizeTarget, setCustomizeTarget] = useState(null) + const [contextMenuPos, setContextMenuPos] = useState<{ x: number; y: number } | null>(null) + const [contextMenuType, setContextMenuType] = useState(null) + const contextMenuRef = useRef(null) + const popoverRef = useRef(null) + const toggleSection = (type: string) => { setCollapsed((prev) => ({ ...prev, [type]: !prev[type] })) } - const getSectionColor = (entry: VaultEntry) => getTypeColor(entry.isA ?? 'Note') + // Build a map of type name → type entry for quick lookup of icon/color + const typeEntryMap = useMemo(() => { + const map: Record = {} + for (const e of entries) { + if (e.isA === 'Type') map[e.title] = e + } + return map + }, [entries]) + + const getSectionColor = (entry: VaultEntry) => { + const typeEntry = typeEntryMap[entry.isA ?? ''] + return getTypeColor(entry.isA ?? 'Note', typeEntry?.color) + } const isActive = (sel: SidebarSelection): boolean => { if (selection.kind !== sel.kind) return false @@ -79,20 +100,90 @@ export const Sidebar = memo(function Sidebar({ entries, selection, onSelect, onS .map((e) => ({ label: e.title + 's', type: e.title, - Icon: FileText, + Icon: resolveIcon(e.icon), + customColor: e.color, })) }, [entries]) + // For built-in types, check if they have custom icon/color overrides from their type entry + const builtInWithOverrides: SectionGroup[] = useMemo(() => { + return BUILT_IN_SECTION_GROUPS.map((sg) => { + const typeEntry = typeEntryMap[sg.type] + if (!typeEntry?.icon && !typeEntry?.color) return sg + return { + ...sg, + Icon: typeEntry?.icon ? resolveIcon(typeEntry.icon) : sg.Icon, + customColor: typeEntry?.color ?? null, + } + }) + }, [typeEntryMap]) + const allSectionGroups = useMemo( - () => [...BUILT_IN_SECTION_GROUPS, ...customSectionGroups], - [customSectionGroups], + () => [...builtInWithOverrides, ...customSectionGroups], + [builtInWithOverrides, customSectionGroups], ) - const renderSection = ({ label, type, Icon }: SectionGroup) => { + // Close context menu on outside click + useEffect(() => { + if (!contextMenuPos) return + const handler = (e: MouseEvent) => { + if (contextMenuRef.current && !contextMenuRef.current.contains(e.target as Node)) { + setContextMenuPos(null) + setContextMenuType(null) + } + } + document.addEventListener('mousedown', handler) + return () => document.removeEventListener('mousedown', handler) + }, [contextMenuPos]) + + // Close customize popover on outside click + useEffect(() => { + if (!customizeTarget) return + const handler = (e: MouseEvent) => { + if (popoverRef.current && !popoverRef.current.contains(e.target as Node)) { + setCustomizeTarget(null) + } + } + document.addEventListener('mousedown', handler) + return () => document.removeEventListener('mousedown', handler) + }, [customizeTarget]) + + const handleContextMenu = useCallback((e: React.MouseEvent, type: string) => { + e.preventDefault() + e.stopPropagation() + setContextMenuPos({ x: e.clientX, y: e.clientY }) + setContextMenuType(type) + }, []) + + const openCustomizePopover = useCallback((type: string) => { + setContextMenuPos(null) + setContextMenuType(null) + setCustomizeTarget(type) + }, []) + + const handleCustomizeIcon = useCallback((icon: string) => { + if (!customizeTarget) return + const typeEntry = typeEntryMap[customizeTarget] + if (typeEntry && onCustomizeType) { + onCustomizeType(customizeTarget, icon, typeEntry.color ?? 'blue') + } + }, [customizeTarget, typeEntryMap, onCustomizeType]) + + const handleCustomizeColor = useCallback((color: string) => { + if (!customizeTarget) return + const typeEntry = typeEntryMap[customizeTarget] + if (typeEntry && onCustomizeType) { + onCustomizeType(customizeTarget, typeEntry.icon ?? 'file-text', color) + } + }, [customizeTarget, typeEntryMap, onCustomizeType]) + + const renderSection = ({ label, type, Icon, customColor }: SectionGroup) => { const items = entries.filter((e) => e.isA === type) const isCollapsed = collapsed[type] ?? false const isTopic = type === 'Topic' const isTypeSection = type === 'Type' + const sectionColor = getTypeColor(type, customColor) + const sectionLightColor = getTypeLightColor(type, customColor) const handlePlusClick = (e: React.MouseEvent) => { e.stopPropagation() @@ -115,9 +206,10 @@ export const Sidebar = memo(function Sidebar({ entries, selection, onSelect, onS )} style={{ padding: '6px 16px', borderRadius: 4, gap: 8 }} onClick={() => onSelect({ kind: 'sectionGroup', type })} + onContextMenu={(e) => handleContextMenu(e, type)} >
- + {label}
@@ -160,8 +252,8 @@ export const Sidebar = memo(function Sidebar({ entries, selection, onSelect, onS style={{ padding: '4px 16px 4px 28px', ...(isActive(isTopic ? { kind: 'topic', entry } : { kind: 'entity', entry }) && { - backgroundColor: getTypeLightColor(entry.isA ?? ''), - color: getSectionColor(entry), + backgroundColor: sectionLightColor, + color: sectionColor, }), }} onClick={() => { @@ -257,6 +349,39 @@ export const Sidebar = memo(function Sidebar({ entries, selection, onSelect, onS
)} + + {/* Context menu (right-click on section header) */} + {contextMenuPos && contextMenuType && ( +
+ +
+ )} + + {/* Customize popover */} + {customizeTarget && ( +
+ setCustomizeTarget(null)} + /> +
+ )} ) }) diff --git a/src/components/TypeCustomizePopover.tsx b/src/components/TypeCustomizePopover.tsx new file mode 100644 index 00000000..83bef593 --- /dev/null +++ b/src/components/TypeCustomizePopover.tsx @@ -0,0 +1,145 @@ +import { useState, type ComponentType } from 'react' +import { + FileText, Wrench, Flask, Target, ArrowsClockwise, Users, CalendarBlank, + Tag, StackSimple, BookOpen, CookingPot, Heart, Star, House, Lightbulb, + Briefcase, Gear, Cube, Leaf, MusicNote, Camera, Airplane, GameController, + PaintBrush, ShoppingCart, GraduationCap, Trophy, ChatCircle, Notebook, + MapPin, Code, Barbell, PawPrint, Pill, Knife, + type IconProps, +} from '@phosphor-icons/react' +import { ACCENT_COLORS } from '../utils/typeColors' +import { cn } from '@/lib/utils' + +/** Curated Phosphor icons (normal weight) for type customization */ +export const ICON_OPTIONS: { name: string; Icon: ComponentType }[] = [ + { name: 'file-text', Icon: FileText }, + { name: 'wrench', Icon: Wrench }, + { name: 'flask', Icon: Flask }, + { name: 'target', Icon: Target }, + { name: 'arrows-clockwise', Icon: ArrowsClockwise }, + { name: 'users', Icon: Users }, + { name: 'calendar-blank', Icon: CalendarBlank }, + { name: 'tag', Icon: Tag }, + { name: 'stack-simple', Icon: StackSimple }, + { name: 'book-open', Icon: BookOpen }, + { name: 'cooking-pot', Icon: CookingPot }, + { name: 'heart', Icon: Heart }, + { name: 'star', Icon: Star }, + { name: 'house', Icon: House }, + { name: 'lightbulb', Icon: Lightbulb }, + { name: 'briefcase', Icon: Briefcase }, + { name: 'gear', Icon: Gear }, + { name: 'cube', Icon: Cube }, + { name: 'leaf', Icon: Leaf }, + { name: 'music-note', Icon: MusicNote }, + { name: 'camera', Icon: Camera }, + { name: 'airplane', Icon: Airplane }, + { name: 'game-controller', Icon: GameController }, + { name: 'paint-brush', Icon: PaintBrush }, + { name: 'shopping-cart', Icon: ShoppingCart }, + { name: 'graduation-cap', Icon: GraduationCap }, + { name: 'trophy', Icon: Trophy }, + { name: 'chat-circle', Icon: ChatCircle }, + { name: 'notebook', Icon: Notebook }, + { name: 'map-pin', Icon: MapPin }, + { name: 'code', Icon: Code }, + { name: 'barbell', Icon: Barbell }, + { name: 'paw-print', Icon: PawPrint }, + { name: 'pill', Icon: Pill }, + { name: 'knife', Icon: Knife }, +] + +const ICON_MAP: Record> = Object.fromEntries( + ICON_OPTIONS.map((o) => [o.name, o.Icon]), +) + +/** Resolves a Phosphor icon name to its component, with fallback to FileText */ +export function resolveIcon(name: string | null): ComponentType { + return (name && ICON_MAP[name]) || FileText +} + +interface TypeCustomizePopoverProps { + currentIcon: string | null + currentColor: string | null + onChangeIcon: (icon: string) => void + onChangeColor: (color: string) => void + onClose: () => void +} + +export function TypeCustomizePopover({ + currentIcon, + currentColor, + onChangeIcon, + onChangeColor, + onClose, +}: TypeCustomizePopoverProps) { + const [selectedColor, setSelectedColor] = useState(currentColor) + const [selectedIcon, setSelectedIcon] = useState(currentIcon) + + const handleColorClick = (key: string) => { + setSelectedColor(key) + onChangeColor(key) + } + + const handleIconClick = (name: string) => { + setSelectedIcon(name) + onChangeIcon(name) + } + + return ( +
e.stopPropagation()} + onContextMenu={(e) => e.stopPropagation()} + > + {/* Color section */} +
COLOR
+
+ {ACCENT_COLORS.map((c) => ( +
+ + {/* Icon section */} +
ICON
+
+ {ICON_OPTIONS.map(({ name, Icon }) => ( + + ))} +
+ + {/* Done button */} +
+ +
+
+ ) +} diff --git a/src/hooks/useVaultLoader.ts b/src/hooks/useVaultLoader.ts index ed49d82a..fee235a8 100644 --- a/src/hooks/useVaultLoader.ts +++ b/src/hooks/useVaultLoader.ts @@ -67,6 +67,10 @@ export function useVaultLoader(vaultPath: string) { setAllContent((prev) => ({ ...prev, [path]: content })) }, []) + const updateEntry = useCallback((path: string, patch: Partial) => { + setEntries((prev) => prev.map((e) => e.path === path ? { ...e, ...patch } : e)) + }, []) + const loadGitHistory = useCallback(async (path: string): Promise => { try { if (isTauri()) { @@ -113,6 +117,7 @@ export function useVaultLoader(vaultPath: string) { allContent, modifiedFiles, addEntry, + updateEntry, updateContent, loadModifiedFiles, loadGitHistory, diff --git a/src/index.css b/src/index.css index b09854ed..08680379 100644 --- a/src/index.css +++ b/src/index.css @@ -69,6 +69,7 @@ --accent-blue-hover: #0D4AD6; --accent-green: #38A169; --accent-orange: #D9730D; + --accent-orange-light: rgba(217, 115, 13, 0.1); --accent-red: #E53E3E; --accent-purple: #805AD5; --accent-yellow: #D69E2E; diff --git a/src/utils/typeColors.ts b/src/utils/typeColors.ts index c2735d3b..34e2433d 100644 --- a/src/utils/typeColors.ts +++ b/src/utils/typeColors.ts @@ -28,12 +28,31 @@ const TYPE_LIGHT_COLOR_MAP: Record = { const DEFAULT_COLOR = 'var(--accent-blue)' const DEFAULT_LIGHT_COLOR = 'var(--accent-blue-light)' -/** Returns the CSS variable for the accent color of a given note type */ -export function getTypeColor(isA: string | null): string { +/** Color key → CSS variable mapping for the design system accent palette */ +export const ACCENT_COLORS: { key: string; label: string; css: string; cssLight: string }[] = [ + { key: 'red', label: 'Red', css: 'var(--accent-red)', cssLight: 'var(--accent-red-light)' }, + { key: 'orange', label: 'Orange', css: 'var(--accent-orange)', cssLight: 'var(--accent-orange-light)' }, + { key: 'yellow', label: 'Yellow', css: 'var(--accent-yellow)', cssLight: 'var(--accent-yellow-light)' }, + { key: 'green', label: 'Green', css: 'var(--accent-green)', cssLight: 'var(--accent-green-light)' }, + { key: 'blue', label: 'Blue', css: 'var(--accent-blue)', cssLight: 'var(--accent-blue-light)' }, + { key: 'purple', label: 'Purple', css: 'var(--accent-purple)', cssLight: 'var(--accent-purple-light)' }, +] + +const COLOR_KEY_TO_CSS: Record = Object.fromEntries( + ACCENT_COLORS.map((c) => [c.key, c.css]), +) +const COLOR_KEY_TO_CSS_LIGHT: Record = Object.fromEntries( + ACCENT_COLORS.map((c) => [c.key, c.cssLight]), +) + +/** Returns the CSS variable for the accent color of a given note type, with optional custom override */ +export function getTypeColor(isA: string | null, customColorKey?: string | null): string { + if (customColorKey && COLOR_KEY_TO_CSS[customColorKey]) return COLOR_KEY_TO_CSS[customColorKey] return (isA && TYPE_COLOR_MAP[isA]) ?? DEFAULT_COLOR } /** Returns the CSS variable for the light/background variant of a given note type's color */ -export function getTypeLightColor(isA: string | null): string { +export function getTypeLightColor(isA: string | null, customColorKey?: string | null): string { + if (customColorKey && COLOR_KEY_TO_CSS_LIGHT[customColorKey]) return COLOR_KEY_TO_CSS_LIGHT[customColorKey] return (isA && TYPE_LIGHT_COLOR_MAP[isA]) ?? DEFAULT_LIGHT_COLOR } From 23daf47353d6ff4ee3dfbcd23a929ec442067282 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Sat, 21 Feb 2026 16:26:32 +0100 Subject: [PATCH 4/4] fix: remove unused getSectionColor function (TS build fix) --- src/components/Sidebar.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 0f112a81..a7388870 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -78,11 +78,6 @@ export const Sidebar = memo(function Sidebar({ entries, selection, onSelect, onS return map }, [entries]) - const getSectionColor = (entry: VaultEntry) => { - const typeEntry = typeEntryMap[entry.isA ?? ''] - return getTypeColor(entry.isA ?? 'Note', typeEntry?.color) - } - const isActive = (sel: SidebarSelection): boolean => { if (selection.kind !== sel.kind) return false if (sel.kind === 'filter' && selection.kind === 'filter') return sel.filter === selection.filter