The window drew its icons with unicode glyphs -- a clip emoji for attach, a
gear, a hamburger, a chevron, a multiplication sign for delete. Those render
in whichever font happens to cover them (colour emoji in a grey toolbar) and
are a box on a machine without that glyph. They now come from the icon theme,
the way every other desktop app gets them.
Icons
- app/src/ui/ThemeIcons.{h,cpp}: a QQuickImageProvider for
image://csttheme/<name>?color=%23rrggbb -- QIcon::fromTheme() in the
desktop's theme (whatever the platform theme set, else breeze-dark, breeze,
hicolor), tinted with QPainter's SourceIn and rasterised at the requested
size and the screen's device pixel ratio. Qt only builds its theme search
paths from XDG once a theme name is set, so the provider fills them itself
(GenericDataLocation/icons, ~/.icons and, on NixOS,
/run/current-system/sw/share/icons, for a shell started outside the desktop
session). An unknown name yields a null image, which QML reports as
Image.Error.
- app/qml/ThemeIcon.qml: the one component that asks for an icon. Every use
still names the old character as `glyph:`, so a host with no icon theme
degrades to exactly what the window looked like before instead of going
blank. installThemeIcons() is called by main() and by --uitest, so the test
loads the same icons the app does.
- Replaced: nav (application-menu), settings (settings-configure), attach
(mail-attachment), copy (edit-copy), session delete (edit-delete), new
session (list-add), expand/collapse (arrow-right/arrow-down), jump to the end
(go-down), the plan checklist (task-complete/box). A tool card also shows
what kind of tool ran -- Theme.toolIcon(): pencil for patch/write, text page
for read, folder for list/glob, magnifier for grep, braces for a shell,
globe for web, list for a plan. Toolbar icons are 22 px (Breeze's toolbar
size), card and row icons 14-16 px.
Colours (the "some text is unreadable" part)
- textFaint #6d757b -> #8d959c: ~3.4:1 -> ~5:1 on the card fill. It carries
hints, provider URLs, session metadata and placeholders, so it is text, not
decoration.
- A selected provider row and the open session row write on the accent fill;
they now use accentText / accentTextDim / okOnAccent instead of faint grey
and green on blue (named "accentText", not "onAccent": QML reads any `onFoo`
property as a signal handler and refuses the assignment).
- The filled buttons (Send, Use this) take their ink from Theme.accentInk.
Composer
- The frame is one Theme.pad around its column on every side (anchors.fill,
implicitHeight + 2*pad). With the top margin alone the last row -- the clip
and Send -- hung over the lower border, which is what put the clip on the
frame instead of inside it.
app/README.md gains an "Icons, colors" section (the provider, why the theme
search paths are filled by hand, the qtsvg requirement for rasterising Breeze,
the tool map, and the contrast rules).
Verified: --uitest 52/52, --selftest OK, and the 0.8.14 window relaunched in
the Plasma session (no QML warnings).
Version 0.8.14.
mode = coding
99 lines
4.2 KiB
QML
99 lines
4.2 KiB
QML
// One place for the look: colors, spacing, fonts. The window, the cards and
|
|
// the dialogs all read from here, so re-theming (or swapping in Kirigami's
|
|
// Kirigami.Theme) is a single file.
|
|
pragma Singleton
|
|
|
|
import QtQuick
|
|
|
|
QtObject {
|
|
// Breeze-dark-ish (the desktop the app is built for), no hard contrast.
|
|
readonly property color bg: "#15181a"
|
|
readonly property color surface: "#1e2225"
|
|
readonly property color surfaceAlt: "#262b2f"
|
|
readonly property color surfaceHi: "#313840"
|
|
readonly property color border: "#343b41"
|
|
readonly property color text: "#eff0f1"
|
|
readonly property color textDim: "#9aa2a8"
|
|
/* Faint is still text: small print (hints, secondary lines, a selected
|
|
* row's detail) has to stay readable on both the background and the
|
|
* accent fill, so it is a step lighter than it looks in a mockup. */
|
|
readonly property color textFaint: "#8d959c"
|
|
readonly property color accent: "#3daee9"
|
|
readonly property color accentDim: "#1d6f95"
|
|
/* What is written *on* the accent fill (a selection, the open session).
|
|
* Named "accentText...", not "onAccent...": QML reads any `onFoo` as a
|
|
* signal handler and refuses the assignment. */
|
|
readonly property color accentText: "#ffffff"
|
|
readonly property color accentTextDim: "#dceefb"
|
|
/* The dark ink a filled button carries (accent, ok, err are all light). */
|
|
readonly property color accentInk: "#12161a"
|
|
readonly property color ok: "#27ae60"
|
|
readonly property color warn: "#f67400"
|
|
readonly property color err: "#da4453"
|
|
/* Green on the accent fill is mud: the "in use" mark needs its own ink. */
|
|
readonly property color okOnAccent: "#d8f5e3"
|
|
|
|
readonly property int radius: 6
|
|
readonly property int radiusSm: 3
|
|
readonly property int gap: 8
|
|
readonly property int gapL: 14
|
|
readonly property int pad: 12
|
|
readonly property int padL: 16
|
|
|
|
readonly property int bodySize: 14
|
|
readonly property int smallSize: 12
|
|
readonly property int monoSize: 13
|
|
readonly property string mono: "monospace"
|
|
|
|
readonly property int sidebarWidth: 268
|
|
readonly property int contentMax: 1100
|
|
|
|
// Layout: below the breakpoint the sidebar slides over the transcript
|
|
// instead of taking a column, and controls stay thumb-sized above it
|
|
// (a finger needs ~34-40 px, a mouse is happy with 26).
|
|
readonly property int compactBreakpoint: 880
|
|
readonly property int touchTarget: 34
|
|
readonly property int segmentHeight: 30
|
|
readonly property int headerHeight: 42
|
|
readonly property int jumpButton: 40
|
|
|
|
// Which Breeze icon stands for a tool: the card shows at a glance what
|
|
// kind of thing ran. Unknown names still get one (no glyph fallbacks for
|
|
// tools -- a missing icon just leaves the name alone).
|
|
function toolIcon(name) {
|
|
const n = String(name).toLowerCase()
|
|
if (n.indexOf("patch") >= 0 || n.indexOf("write") >= 0 || n.indexOf("edit") >= 0)
|
|
return "document-edit"
|
|
if (n.indexOf("read") >= 0 || n.indexOf("cat") >= 0)
|
|
return "text-x-generic"
|
|
if (n.indexOf("list_dir") >= 0 || n.indexOf("glob") >= 0 ||
|
|
n.indexOf("find") >= 0 || n.indexOf("dir") >= 0 || n.indexOf("file") >= 0)
|
|
return "folder-open"
|
|
if (n.indexOf("grep") >= 0 || n.indexOf("search") >= 0)
|
|
return "edit-find"
|
|
if (n.indexOf("bash") >= 0 || n.indexOf("shell") >= 0 ||
|
|
n.indexOf("command") >= 0 || n.indexOf("run") >= 0)
|
|
return "code-context"
|
|
if (n.indexOf("web") >= 0 || n.indexOf("http") >= 0 || n.indexOf("fetch") >= 0)
|
|
return "internet-services"
|
|
if (n.indexOf("todo") >= 0 || n.indexOf("plan") >= 0)
|
|
return "format-list-unordered"
|
|
if (n.indexOf("ask") >= 0 || n.indexOf("question") >= 0 || n.indexOf("notif") >= 0)
|
|
return "help-hint"
|
|
return "code-context"
|
|
}
|
|
|
|
// The tool output / diff line colors, used by ToolCard.
|
|
function lineColor(line) {
|
|
if (line.startsWith("@@"))
|
|
return accent
|
|
if (line.startsWith("+++") || line.startsWith("---"))
|
|
return textDim
|
|
if (line.startsWith("+"))
|
|
return ok
|
|
if (line.startsWith("-"))
|
|
return err
|
|
return text
|
|
}
|
|
}
|