Skip to content

Give elements an icon and a colour, from icon packs Groma.md publishes - #118

Open
MrLesk wants to merge 13 commits into
task-568-plugins-jsonfrom
task-561-icon-packs
Open

MrLesk wants to merge 13 commits into
task-568-plugins-jsonfrom
task-561-icon-packs

Conversation

@MrLesk

@MrLesk MrLesk commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Implements TASK-561. Elements can carry an icon and an accent colour (groma edit <id> --icon <name> --colour <name> and the web details form), stored as groma.icon and groma.colour and preserved by scans. Icons come from icon packs: packages that declare their SVGs under groma.icons and install with groma plugin add into the icons section of groma/plugins.json. A name resolves through the installed packs or is an emoji, and an unknown name shows up in groma lint.

The map, the cover export and both details panes draw the icon and colour the same way in every theme. A legend lists the colours in use with a count each. plugins/icons/architecture is a first pack of twelve generic icons, built and released like the scanner packages, and docs/icons.md explains how to make and publish one.

A Codex agent (GPT-6.1 Sol) built this live during the Devoxx Belgium keynote on 9 October, working TASK-561 in one checkout it shared with five other agents. This branch holds only this task's changes: I matched every changed line to the agent session that wrote it and rebuilt the task on a fresh branch from main.

Stacked on #117. The agent built icon packs on TASK-568's plugin system: plugin kinds, plugins.json and groma plugin add. The two share new files, so this PR shows only the icon work on top of that branch.

Acceptance criteria, as the agent left them

  • An element can carry an icon name and an accent colour in its groma fields, set with groma edit --icon --colour and in the web details form, validated like the other fields, documented in docs/component-markdown.md, and preserved by scans.
  • An icon pack is a package whose package.json declares under groma.icons the SVG icons it ships. groma plugin add installs a pack: it resolves npm, a local folder or git the way scanners are resolved and records the pack in the icons section of /plugins.json. groma plugin list and groma plugin remove manage it, and the web settings dialog shows the installed packs.
  • An icon name resolves through the installed packs or is an emoji. An unknown name leaves the building without an icon and appears in groma lint as a finding, never as a failed map.
  • The web map draws the icon and the accent colour on the building and its label the same way in light, dark and blueprint. The web details pane and the terminal details pane show them. The cover export includes the icon and the colour.
  • A legend on the map lists the accent colours in use with a count each, and hides when no element has one.
  • One pack lives under plugins/icons/ with at least twelve generic architecture icons (database, queue, cache, browser, mobile app, service, scheduler, file store, mail, identity, gateway, search), built and published the same way as the scanner packages, and docs/ describes how to make and publish a pack.

Validation

On stage the agents were told not to install, build or run tests, so these are the first runs of this code.

Overlap with the other PRs from this run

The criticality PR (#122) adds its field next to these in the element model, the emitter, groma edit, the sheet, the building painter, the details panes and the component Markdown contract. Both PRs add separate fields side by side, so merging the second one means keeping both. render.ts is shared with the playback (#121) and metrics (#123) PRs, and write-commands.ts with the plans PR (#120).

@MrLesk
MrLesk added this pull request to stack #119 October 9, 2026 09:13
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T19:54:02.868601Z aa470d5 New commits
🔒 Security Review ✅ Completed 2026-10-09T09:18:34.878445Z 1b737ef PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1b737ef81f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/icon-packs.ts Outdated
const config = await readScannerConfig(root)
if (!replacing && (config.icons ?? []).some(pack => pack.id === id)) throw new Error(`Icon pack already configured: ${id}`)
if (config.scanners.some(pack => pack.id === id) || config.workSources?.some(pack => pack.id === id)) throw new Error(`Plugin id already configured: ${id}`)
await writeScannerConfig(root, { ...config, icons: [...(config.icons ?? []).filter(pack => pack.id !== id), { id, source }] })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve icon-pack precedence when updating

When two configured packs expose the same bare icon name, loadIconCatalog intentionally lets the first pack win, but replacing a pack removes it and appends it to the end of the array. Running groma plugin update on the first pack therefore silently changes every unqualified reference such as database to the competing pack's artwork. Replace the entry in place so an update does not alter configured resolution order.

AGENTS.md reference: AGENTS.md:L265-L267

Useful? React with 👍 / 👎.

Comment thread src/viewers/web/iso/painting/text.ts Outdated
): SvgNode {
const layout = surfaceLabelLayout(text, size, zoom, view)
return node('g', { transform: planeMatrix('ground', text.origin, view) }, 'label surface-label', [
...(iconSvg ? [iconImage(iconSvg, layout.x - layout.fontSize * 1.2, layout.baseline - layout.fontSize * 0.9, layout.fontSize)] : []),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep icons inside the reserved surface-label band

For system and container elements with short titles, this places the icon to the left of a title whose origin was already centered without accounting for the icon. Although placement adds three cells to the surface width, that space is split across both sides, while the icon is shifted left by roughly 1.2 font sizes; at larger adaptive label sizes it consequently extends beyond the reserved band, where it can overlap adjacent content or be cropped from an export. Center and size the combined icon-and-title group within the band to satisfy the map and cover rendering criterion.

AGENTS.md reference: AGENTS.md:L265-L267

Useful? React with 👍 / 👎.

Comment thread src/core.ts
Comment on lines +84 to +85
...(element.icon === undefined ? {} : { icon: element.icon }),
...(element.colour === undefined ? {} : { colour: element.colour }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include appearance changes in revision comparisons

When a component changes only its icon or colour between revisions, compareArchitecture still marks it unchanged because its ownContent fingerprint does not include either newly propagated field. If another field changes at the same time, the component is marked modified but the comparison reasons and detail view still omit the appearance change. Include icon and colour in comparison detection and presentation so history reports these authored changes.

AGENTS.md reference: AGENTS.md:L265-L267

Useful? React with 👍 / 👎.

Comment thread src/scanner/modules/config.ts Outdated
if (config.useGitignore !== undefined && typeof config.useGitignore !== 'boolean') {
throw new Error(`${sourceFilename} useGitignore must be true or false`)
}
const icons = configuredIcons(config.icons, sourceFilename)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject icon IDs that collide with other plugin kinds

When a documented plugins.json configuration is edited directly, an icon pack can reuse a scanner or work-source ID because configuredIcons validates only duplicates within the icons array and the later scanner-ID set is not seeded from icons or work sources. ID-based operations then become ambiguous: removePlugin and updatePlugin select the icon entry first, while scanner restoration can become unreachable. Validate plugin IDs across all sections while parsing the configuration, not only on CLI additions.

AGENTS.md reference: AGENTS.md:L265-L267

Useful? React with 👍 / 👎.

Comment on lines +13 to +14
const style = classes.includes('text') ? `fill:${colour}`
: classes.includes('face') || classes.includes('ground') ? `stroke:${colour};fill:${accentTint(colour)}` : undefined

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Let map state styling override authored accents

When an element has an authored colour, these inline fill and stroke declarations outrank all of the existing stylesheet rules for lifecycle and interaction state. A coloured draft therefore renders with solid faces instead of the required ghost outline, and selected, flow-lit, or comparison-marked elements retain their authored strokes and fills instead of the highlight or diff colours. Represent the accent in a way that the state rules can override, while retaining explicit values for static exports.

AGENTS.md reference: AGENTS.md:L318-L319

Useful? React with 👍 / 👎.

Comment thread src/types.ts
Comment on lines +99 to +100
icon?: string
colour?: string

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Protect appearance metadata during combines

If an otherwise empty scanned element has an authored icon or colour, groma edit <target> --combine <source> accepts it because requireCombinableSources checks only group and technology; the source document is then removed and both appearance fields are silently lost. Treat these new fields as authored metadata in the combinability guard, or define an explicit merge rule, just as the existing authored fields are protected.

AGENTS.md reference: AGENTS.md:L226-L229

Useful? React with 👍 / 👎.

MrLesk added a commit that referenced this pull request Oct 11, 2026
Update the main package dependencies and use Bun 1.4.3 for development
and all Bun workflows. Adapt Groma's terminal lifecycle to OpenTUI 0.6.0
so selecting a welcome action waits for terminal suspension, and closing
a terminal view waits for restoration before streams are reused or the
next welcome screen opens.

| Package | Before | After |
| --- | --- | --- |
| `@opentui/core` | 0.5.12 | 0.6.0 |
| `ignore` | 7.0.10 | 7.0.12 |
| `@biomejs/biome` | 2.5.14 | 2.5.15 |
| `@types/node` | 26.6.2 | 26.6.5 |
| `typescript` | 7.1.0-dev.20260924.1 | 7.1.0-dev.20261011.1 |

Welcome now awaits `suspend()` before exposing an action. Its CLI owner
awaits session cleanup before reopening. The map returns OpenTUI's
`renderer.closed` directly, removing its separate early completion
promise. Standalone scanner settings and viewer setup failures also
await terminal restoration.

Scanner, example, and other workspace package files and versions are
unchanged. Keep workspace links, the Parcel watcher patch, and the
existing TypeScript compiler families. Root TypeScript uses the newest
preview in its existing 7.1 family; the lockfile retains the scanner's
previous compiler separately. Other root packages already match latest
stable releases, and `@types/bun` 1.4.2 remains latest published.

Release checks: [OpenTUI breaking
changes](https://github.com/anomalyco/opentui/releases/tag/v0.6.0),
[ignore patch
changes](kaelzhang/node-ignore@7.0.10...7.0.12),
[Biome
2.5.15](https://github.com/biomejs/biome/releases/tag/@biomejs%2Fbiome@2.5.15).
Existing framebuffer and palette APIs continue to work; no unrelated Bun
features or new architecture concepts are introduced.

Validation on macOS arm64 with Bun 1.4.3:

- Three concurrent lifecycle regressions failed before the fix and
passed afterward: welcome suspension, welcome close, and map close must
wait for their transitions.
- `bun run check`: 16 Node tests and 779 Bun tests passed; 51 optional
scanner/package tests skipped, zero failures.
- `bun install --frozen-lockfile`: no changes. Standalone build passed.
- Compiled `tui-test` checks passed: welcome → settings child → welcome
→ quit; map root → container → arrow selection → details → 200×60 resize
→ Ctrl+C. Screenshots were visually checked.
- Scanner/example files remain unchanged; `git diff --check` passed and
`groma/` is unchanged.

Also pin the architecture comparison action to [groma.md-action PR
#6](MrLesk/groma.md-action#6), commit
`b5eb98ce51df144ba276182a5b80a7a2ce6d11a3`. Comparison preparation no
longer requires `scanners.json`; export reads committed architecture and
source from an empty temporary working root without loading project
scanner hooks or changing configuration. The released scan path stays
unchanged until the plugin CLI is released.

Action validation: all 26 tests and both GitHub Check runs pass. Real
exports pass with Groma 0.6.6 and the plugin-branch CLI, including the
exact commits that previously failed in PR #118. Because comparison runs
use `pull_request_target`, this pin takes effect after this PR is
merged.

Tracks TASK-579.
@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Groma architecture comparison

Changes Added Modified Removed
Components 1 23 0
Relationships 0 0 0

Open before / after comparison

Compared 34a111a (merge base) → aa470d5.

MrLesk added 11 commits October 11, 2026 17:45
# Conflicts:
#	src/plugin-management.ts
#	src/scanner/modules/config.ts
#	src/scanner/modules/package.ts
#	src/scanner/modules/published.ts
#	src/scanner/modules/settings-model.ts
#	src/scanner/modules/settings.ts
#	src/viewers/web/scanners/settings.ts
#	test-bun/scanner-update.test.ts
# Conflicts:
#	groma/systems/groma-md/containers/cli/components/package.md
#	src/plugin-management.ts
#	src/scanner/modules/config.ts
#	src/scanner/modules/inventory.ts
#	src/scanner/modules/package.ts
#	src/scanner/modules/published.ts
#	src/scanner/modules/settings.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aa470d5789

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (palette === undefined) {
return rules('.accented', 'currentColor',
`color-mix(in srgb, currentColor ${percent(ACCENT_TINT)}, var(--paper))`,
`color-mix(in srgb, var(--ink) ${percent(ACCENT_TITLE)}, currentColor)`)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep custom hex accents readable on map labels

For supported custom hex colours, the fixed 45% title mix does not guarantee the readability claimed by the appearance contract: in the light theme #ffffff produces a #9c9da1 title on a white surface (about 2.71:1 contrast), while in the dark theme #000000 produces about 3.46:1. The test covers only the curated named palette, so valid #RRGGBB input can make map and cover titles difficult to read; derive the title colour or mix ratio from the actual accent and theme.

AGENTS.md reference: AGENTS.md:L226-L229

Useful? React with 👍 / 👎.

}

export const elementTitleCss = `
#details h1 .element-name { color: var(--element-colour, inherit); }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve contrast for accented details headings

When a valid accent matches the current background, this raw colour makes the selected element's name disappear—for example, --colour '#ffffff' in the light theme or #000000 in the dark theme. Some named accents such as yellow also have weak contrast on the light details surface. Use a theme-aware readable derivative for heading text while retaining the authored colour for the swatch and map outline.

AGENTS.md reference: AGENTS.md:L226-L229

Useful? React with 👍 / 👎.

This branch was successfully deployed

1 active deployment
github-pages — aa470d57 Deployed Oct 11, 2026 by MrLesk via deploy #212
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant