diff --git a/docs/decisions.md b/docs/decisions.md index 1d43e188..1a01f5ba 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -4865,6 +4865,19 @@ prototypes-lazurio `1acd615` (`app/v1/src/Rail.tsx`, `Switcher.tsx`, `EnvPicker. before). Every interface icon, the shell's and the Launchpad page's, is drawn at the wireframe's stroke of 1.6 (was 1.5). As the glyph takes the picker's ink, a kind's icon is light on a dark host without roles (it stayed near-black, issue #169). + *Addendum 2026-10-11 (Anna):* the Launchpad's column narrows from 264 to 256 px (four + grid modules) with 13.5 px entries and a 16 px title, and the picker's name drops to + 14 px (`--lz-size-meta`), so a name like "MacBook-Air-2" still fits beside the gear. + Text has three levels, after ClickUp (measured there: #202020, #646464 and + #838383-#8d8d8d): ink for what you act on (a tool's name, a button, the active entry); + muted `gray-500` for what explains it (a settings row's description at 14 px, a + group's label such as Oblíbené or a Tools tier, an inactive entry of the column); and + quiet, halfway between `gray-500` and `gray-400` (3.7:1, as ClickUp's timestamps), + only for text that need not be read (a hint, a neutral state, "Podrobnosti"). + Weights between the design system's 400 and 600, since Inter Tight is variable and + read thin beside ChatGPT's 14 px system text: a description at 450, a group's label + and an inactive entry at 500; names, buttons and the active entry stay at 600. + Proposed to the design system as roles for every app. 6. **The keys, with or without the field.** ↓ and ↑ (wrapping), Home, End, Enter and Escape work in the list. Without the search field the focus lands on the current Environment, else the first (on "Všechny Organizace" where the space has none), and diff --git a/docs/launchpad-development.md b/docs/launchpad-development.md index 029e2034..332d18be 100644 --- a/docs/launchpad-development.md +++ b/docs/launchpad-development.md @@ -1597,12 +1597,18 @@ change (without them; the last known sign-ins stay on the cards) and on "Refresh status" (with them again), and shows: - no introduction (root decision 0188, Matěj 2026-10-04: no paragraph that only - restates the page); each tier's note is one short line: "Bez nich tu nic nefunguje.", - "Hodí se skoro každému.", "Zapni jen to, co používáš."; + restates the page), no note under a tier and no line on when the tools were read + (Anna 2026-10-11: each only restated what the page shows); the tier names are quieter + than the tool names they group (muted, 13 px, weight 500; the levels of text are in + decision F36's addendum of 2026-10-11); - on a shared Environment (the Team preset) the warning that signed-in accounts are shared by all operators; it is repeated in the confirmation of an enable; - three groups, **Required**, **Recommended** and **Optional**, in catalog order, each - a settings group of rows. A row carries on the left the tool's real name (gh, + a settings group of rows. A row carries on the left the tool's logo (Anna 2026-10-11: known at first sight; + `src/tools/icons.ts`, bundled simple-icons, CC0, drawn by the same `appMark` as + Integrace; Composio and Codex carry the vendor's own bundled SVG, the OpenAI Blossom + for Codex; executor, without one, shows its first + letter), its real name (gh, composio, bitwarden, wacli, gogcli, neon: what an agent says and what is searched for), one plain sentence on what it is for (root decision 0188; `toolDescription` in `tools-view.ts`, a page-only text: the catalog's `activation.purpose` is rendered into @@ -1993,7 +1999,7 @@ the Launchpad builds no viewer of its own. Integrace and the Marketplace hides it, the rail staying, and shows it again; while the page steps aside for the panel, the toggle stands in the panel's strip. Its right edge is a divider like the panel's, 208–480 px and never leaving the page less than - 400 px, double-click for 264 px. Below 768 px it stays the sheet opened by the button + 400 px, double-click for 256 px. Below 768 px it stays the sheet opened by the button at the top of the page. - **Remembered in this browser**: the panel's width or all of the room, the column's width and whether it is hidden, as one `localStorage` entry, `lazurio.layout`, read diff --git a/src/launchpad/app-mark.ts b/src/launchpad/app-mark.ts new file mode 100644 index 00000000..7ce5ff3d --- /dev/null +++ b/src/launchpad/app-mark.ts @@ -0,0 +1,37 @@ +import type { CatalogIcon } from "../integrations/catalog-schema"; +import type { MarkSvg } from "../tools/icons"; +import { monogram } from "./integrations-view"; + +/** + * The mark of an app or a tool: its bundled logo in a white square, else its + * first letter. One drawing for Integrace and Settings → Tools, so the same + * app looks the same on both pages. A logo is a simple-icons path in its + * colour, or a vendor's own bundled SVG (Composio). + */ +export function appMark( + name: string, + icon: CatalogIcon | MarkSvg | undefined, +): HTMLElement { + const holder = document.createElement("span"); + holder.className = "integration-mark"; + holder.setAttribute("aria-hidden", "true"); + if (icon === undefined) { + holder.classList.add("integration-mark-letter"); + holder.textContent = monogram(name); + return holder; + } + if ("svg" in icon) { + // A bundled constant, parsed as an SVG document, never as HTML. + const parsed = new DOMParser().parseFromString(icon.svg, "image/svg+xml"); + holder.append(document.importNode(parsed.documentElement, true)); + return holder; + } + const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + svg.setAttribute("viewBox", "0 0 24 24"); + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); + path.setAttribute("d", icon.path); + path.setAttribute("fill", `#${icon.hex}`); + svg.append(path); + holder.append(svg); + return holder; +} diff --git a/src/launchpad/index.html b/src/launchpad/index.html index 430ba982..8bd61fe6 100644 --- a/src/launchpad/index.html +++ b/src/launchpad/index.html @@ -57,6 +57,20 @@ --input: var(--lz-line); --muted: var(--lz-gray-25); --muted-fg: var(--lz-ink-muted); + /* Three levels of text, after ClickUp (Anna 2026-10-11; measured there: + * #202020, #646464 and #838383-#8d8d8d): ink for what you act on (a name, + * the active entry); muted for what explains it (a description, a group's + * label, an inactive entry); quiet for the lowest priority (a hint, a + * neutral state, "Podrobnosti"), halfway between gray-500 and gray-400 at + * 3.7:1, as ClickUp's own timestamps, so used only for text that need not + * be read. */ + --quiet-fg: color-mix(in srgb, var(--lz-gray-500), var(--lz-gray-400)); + /* Weights between the design system's 400 and 600 (Inter Tight is + * variable): text people read at 450, so the tight face does not read thin + * beside system fonts; an entry of the column and a group's label at 500, + * as ClickUp's sidebar; names, buttons and the active entry stay at 600. */ + --weight-text: 450; + --weight-label: 500; --hover: var(--lz-gray-100); --link: var(--lz-accent); --primary: var(--lz-accent-button); @@ -76,7 +90,7 @@ --radius-card: var(--lz-radius-md); --font-sans: var(--lz-font-sans); --font-mono: var(--lz-font-mono); - --column-width: 264px; + --column-width: 256px; --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23707070' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); } *, *::before, *::after { box-sizing: border-box; } @@ -129,18 +143,18 @@ .column { position: sticky; top: 0; z-index: 20; display: flex; flex: none; flex-direction: column; gap: 10px; width: var(--column-width); height: 100vh; padding: 16px 10px 10px; background: var(--lz-gray-25); border-right: 1px solid var(--lz-line-faint); } .column-apps, .column-settings { display: flex; flex: 1; min-height: 0; flex-direction: column; gap: 10px; } .column-scroll { display: flex; flex: 1; min-height: 0; flex-direction: column; gap: 14px; overflow-y: auto; } -.column-title { padding: 0 8px; font-size: 18px; font-weight: var(--lz-weight-title); letter-spacing: var(--lz-track-title); } +.column-title { padding: 0 8px; font-size: 16px; font-weight: var(--lz-weight-title); letter-spacing: var(--lz-track-title); } .column-label { padding: 4px 10px 6px; color: var(--muted-fg); font-size: 13px; } .column-favorites { display: grid; gap: 1px; padding-bottom: 8px; border-bottom: 1px solid var(--lz-line-faint); } .column-hint { padding: 2px 10px 4px; color: var(--muted-fg); font-size: 13px; } .column-foot { display: grid; gap: 8px; padding-top: 8px; border-top: 1px solid var(--lz-line-faint); } .column-foot:not(:has(> :not([hidden]))) { display: none; } .menu { display: grid; gap: 1px; margin: 0; padding: 0; list-style: none; } -.menu-item { display: flex; align-items: center; gap: 10px; min-width: 0; height: 34px; padding: 0 10px; border-radius: var(--lz-radius-sm); color: var(--fg); font-size: 14px; text-decoration: none; } +.menu-item { display: flex; align-items: center; gap: 10px; min-width: 0; height: 32px; padding: 0 10px; border-radius: var(--lz-radius-sm); color: var(--fg); font-size: 13.5px; text-decoration: none; } a.menu-item:hover, button.menu-item:hover { background: var(--lz-gray-50); } -button.menu-item { width: 100%; border: 0; background: transparent; font-weight: 400; cursor: pointer; } +button.menu-item { width: 100%; border: 0; background: transparent; font-weight: var(--weight-label); cursor: pointer; } .menu-item[aria-current="page"] { background: var(--hover); font-weight: var(--lz-weight-akce); } -.menu-item .icon { width: 17px; height: 17px; } +.menu-item .icon { width: 16px; height: 16px; } .menu-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .menu-stone { display: block; flex: none; width: 20px; height: 20px; } .menu-mark { display: grid; flex: none; width: 20px; height: 20px; place-items: center; color: var(--muted-fg); } @@ -285,8 +299,8 @@ .row-copy { display: grid; gap: 3px; min-width: 0; } .row-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem; min-height: 1.25rem; color: var(--fg); font-size: 15px; font-weight: var(--lz-weight-akce); } .row-title code { color: var(--muted-fg); font-weight: 400; } -.row-desc { max-width: 40rem; color: var(--muted-fg); font-size: 13.5px; line-height: 1.45; } -.row-status { color: var(--muted-fg); font-size: 12.5px; } +.row-desc { max-width: 40rem; color: var(--muted-fg); font-size: 14px; font-weight: var(--weight-text); line-height: 1.45; } +.row-status { color: var(--quiet-fg); font-size: 13px; } .row-control { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-start; gap: 8px; min-width: 0; } @container (min-width: 32rem) { .row-control { justify-content: flex-end; } } dl.card { margin: 0; } @@ -298,7 +312,7 @@ .callout[data-kind="warning"], .tools-warning { border: 1px solid var(--warning-border); border-radius: var(--radius-control); background: var(--warning-bg); color: var(--fg); padding: 10px 14px; font-size: 14px; } .callout[data-kind="warning"] strong { color: var(--warning-fg); } .toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; color: var(--muted-fg); font-size: 12px; } -details.technical > summary, details.row-details > summary { display: inline-flex; align-items: center; gap: 0.25rem; cursor: pointer; color: var(--muted-fg); font-size: 13px; list-style: none; border-radius: 4px; } +details.technical > summary, details.row-details > summary { display: inline-flex; align-items: center; gap: 0.25rem; cursor: pointer; color: var(--quiet-fg); font-size: 13px; list-style: none; border-radius: 4px; } details.technical > summary::-webkit-details-marker, details.row-details > summary::-webkit-details-marker { display: none; } details.technical > summary::before, details.row-details > summary::before { content: ""; width: 0.75rem; height: 0.75rem; background: var(--chevron) center / 0.75rem no-repeat; transform: rotate(-90deg); transition: transform 0.15s; } details[open].technical > summary::before, details[open].row-details > summary::before { transform: none; } @@ -524,6 +538,15 @@ .integration-mark { display: grid; flex: none; width: 40px; height: 40px; place-items: center; border: 1px solid var(--lz-line-faint); border-radius: var(--lz-radius-sm); background: var(--lz-white); } .integration-mark svg { width: 24px; height: 24px; } .integration-mark-letter { color: var(--muted-fg); font-weight: var(--lz-weight-akce); font-size: 17px; background: var(--lz-gray-25); } +/* Settings → Tools: the tool's logo beside its name (Anna 2026-10-11), and + * the tier names quieter than the tool names they group. */ +.row-ident { display: flex; align-items: flex-start; gap: 12px; min-width: 0; } +.row-ident > .integration-mark { margin-top: 1px; } +#tools .group-head h2, .column-label { color: var(--muted-fg); font-size: 13px; font-weight: var(--weight-label); } +.column-hint, .menu-soon { color: var(--quiet-fg); } +.menu-item { color: var(--muted-fg); font-weight: var(--weight-label); } +.menu-item[aria-current="page"] { font-weight: var(--lz-weight-akce); } +a.menu-item:hover, button.menu-item:hover, .menu-item[aria-current="page"] { color: var(--fg); } .integration-text { display: grid; gap: 2px; min-width: 0; } .integration-name { font-size: 15px; font-weight: var(--lz-weight-akce); } .integration-line { color: var(--muted-fg); font-size: 13px; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; } @@ -966,7 +989,6 @@