From 3d07487e76990de75c5382d14e9935b78a2d77ca Mon Sep 17 00:00:00 2001 From: swithek <52840391+swithek@users.noreply.github.com> Date: Sun, 6 Sep 2026 17:37:33 +0200 Subject: [PATCH] update doc title icon list --- web/CLAUDE.md | 2 +- .../editor/IconPickerProvider.nuxt.test.ts | 10 +- .../components/editor/IconPickerProvider.vue | 7 +- web/app/utils/icon.test.ts | 44 +- web/app/utils/icon.ts | 913 ++++++++++++++---- web/nuxt.config.ts | 7 +- web/package.json | 7 + web/pnpm-lock.yaml | 70 ++ 8 files changed, 855 insertions(+), 205 deletions(-) diff --git a/web/CLAUDE.md b/web/CLAUDE.md index 7eb44491..d112f236 100644 --- a/web/CLAUDE.md +++ b/web/CLAUDE.md @@ -116,7 +116,7 @@ Form validation (vee-validate) uses its own internal messages — see [README.md - **shadcn-vue** components live in [app/components/shadcn/ui/](app/components/shadcn/ui/) with prefix `ShadcnUi`. Configured in [components.json](components.json). - **Tailwind v4** via `@tailwindcss/vite`, theme in [app/assets/css/main.css](app/assets/css/main.css). -- Icons via `@nuxt/icon` (CSS mode) — only icons listed by `selectableIconList()` in [app/utils/icon.ts](app/utils/icon.ts) are bundled client-side. Custom SVGs live in [app/assets/custom-icons/](app/assets/custom-icons/) and are reachable under the `custom-icons:` prefix. +- Icons via `@nuxt/icon` (CSS mode). The client bundle holds the icons the scanner finds in app code, plus — on the desktop build only — the document title icons listed by `selectableIconList()` in [app/utils/icon.ts](app/utils/icon.ts). The web build resolves those on demand through the server so they stay off the startup path. Custom SVGs live in [app/assets/custom-icons/](app/assets/custom-icons/) and are reachable under the `custom-icons:` prefix. - **Open a11y bug: dialogs have no `DialogDescription`.** Every modal renders its description as a plain `

`, so the `aria-describedby` reka-ui puts on `DialogContent` points at nothing — screen readers get no description, and reka-ui warns once per mount. **This is scheduled to be fixed, not accepted.** The warning is suppressed in [vitest.config.ts](vitest.config.ts)'s `onConsoleLog` purely to keep test output readable in the meantime; that branch comes out with the fix, and the warning is still visible in `nuxt dev`. Note that routing the text through `ShadcnUiDialogDescription` is *not* a drop-in fix: the wrapper runs its class through `cn()`, and default tailwind-merge reads the custom `text-2sm` as a colour utility and drops it in favour of the wrapper's `text-sm`. Closing this needs either reka-ui's `DialogDescription` imported directly (no `cn()`) or `extendTailwindMerge` teaching `cn()` the custom text scale. ## Formatting & TS diff --git a/web/app/components/editor/IconPickerProvider.nuxt.test.ts b/web/app/components/editor/IconPickerProvider.nuxt.test.ts index 196e3df8..c6426b00 100644 --- a/web/app/components/editor/IconPickerProvider.nuxt.test.ts +++ b/web/app/components/editor/IconPickerProvider.nuxt.test.ts @@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, it, vi } from "vitest" import IconPickerProvider from "./IconPickerProvider.vue" import IconPicker from "./IconPicker.vue" import { mountUnderTooltipProvider, t } from "~/components/test-helpers" +import { selectableIcons } from "~/utils/icon" // the real list is virtualized off the rendered row heights, which // happy-dom reports as zero — nothing would ever be drawn. The stand-in @@ -128,10 +129,11 @@ describe("", { concurrent: false }, () => { await openPicker(wrapper) const total = popover()?.querySelectorAll("button").length ?? 0 - await typeFilter("tag") + await typeFilter("git") const filtered = popover()?.querySelectorAll("button").length ?? 0 + expect(total).toBe(selectableIcons.length) expect(filtered).toBeGreaterThan(0) expect(filtered).toBeLessThan(total) }) @@ -139,13 +141,13 @@ describe("", { concurrent: false }, () => { it("hands the chosen icon to the child that asked", async ({ expect }) => { const wrapper = await mountProvider() await openPicker(wrapper) - await typeFilter("tag 2") + await typeFilter("git merge") popover()?.querySelectorAll("button")[0]?.click() await nextTick() expect(wrapper.findComponent(IconPicker).emitted("select")).toEqual([ - ["mingcute:tag-2-fill"], + ["mingcute:git-merge-fill"], ]) expect(popoverOpen()).toBe(false) }) @@ -207,7 +209,7 @@ describe("", { concurrent: false }, () => { it("clears the filter each time it opens", async ({ expect }) => { const wrapper = await mountProvider() await openPicker(wrapper) - await typeFilter("tag") + await typeFilter("git") document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" })) await nextTick() diff --git a/web/app/components/editor/IconPickerProvider.vue b/web/app/components/editor/IconPickerProvider.vue index 61817d3f..9588aeb5 100644 --- a/web/app/components/editor/IconPickerProvider.vue +++ b/web/app/components/editor/IconPickerProvider.vue @@ -29,14 +29,13 @@ const tooltipOpen = ref(null) const icons = computed< { - normalName: string + name: string id: string }[] >(() => { return selectableIcons.filter((icon) => { return ( - !iconFilter.value || - icon.prettyName.includes(iconFilter.value.toLowerCase()) + !iconFilter.value || icon.name.includes(iconFilter.value.toLowerCase()) ) }) }) @@ -210,7 +209,7 @@ function handleEscape(e: KeyboardEvent) { sticky="always" class="px-1.5 py-1" > - {{ item.normalName }} + {{ item.name }} diff --git a/web/app/utils/icon.test.ts b/web/app/utils/icon.test.ts index 77a81770..e798f464 100644 --- a/web/app/utils/icon.test.ts +++ b/web/app/utils/icon.test.ts @@ -1,6 +1,28 @@ +import type { IconifyJSON } from "@iconify/types" +import { icons as cib } from "@iconify-json/cib" +import { icons as deviconPlain } from "@iconify-json/devicon-plain" +import { icons as fileIcons } from "@iconify-json/file-icons" +import { icons as fluent } from "@iconify-json/fluent" +import { icons as mdi } from "@iconify-json/mdi" +import { icons as mingcute } from "@iconify-json/mingcute" +import { icons as ph } from "@iconify-json/ph" +import { icons as ri } from "@iconify-json/ri" +import { icons as simpleIcons } from "@iconify-json/simple-icons" import { describe, it } from "vitest" import { selectableIconList, selectableIcons } from "./icon" +const collections: Record = { + cib, + "devicon-plain": deviconPlain, + "file-icons": fileIcons, + fluent, + mdi, + mingcute, + ph, + ri, + "simple-icons": simpleIcons, +} + describe("selectableIconList", () => { it("returns the id of every selectable icon in order", ({ expect }) => { const list = selectableIconList() @@ -9,9 +31,23 @@ describe("selectableIconList", () => { expect(list.length).toBeGreaterThan(0) }) - it("returns only prefixed icon ids", ({ expect }) => { - for (const id of selectableIconList()) { - expect(id).toMatch(/^mingcute:/) - } + it("lists every icon once", ({ expect }) => { + const ids = selectableIconList() + + expect(new Set(ids).size).toBe(ids.length) }) + + it.for(selectableIconList())( + "names %s after a glyph its installed collection ships", + (id, { expect }) => { + const [prefix, name] = id.split(":") as [string, string] + const collection = collections[prefix] + + expect(collection).toBeDefined() + expect( + name in (collection?.icons ?? {}) || + name in (collection?.aliases ?? {}), + ).toBe(true) + }, + ) }) diff --git a/web/app/utils/icon.ts b/web/app/utils/icon.ts index 1120a46c..57c587a5 100644 --- a/web/app/utils/icon.ts +++ b/web/app/utils/icon.ts @@ -1,238 +1,769 @@ -// Curated list of MingCute fill icons for knowledge base / technical team use -const mingCuteIcons = [ - // Documents & Files +// icons a document can carry in its title, in the order the picker shows +// them. +const icons: (string | [id: string, name: string])[] = [ + // documents & knowledge "mingcute:document-2-fill", "mingcute:document-3-fill", + "mingcute:documents-fill", + "mingcute:doc-fill", + "mingcute:file-code-fill", "mingcute:file-certificate-fill", "mingcute:file-check-fill", - "mingcute:file-code-fill", - "mingcute:file-download-fill", - "mingcute:file-export-fill", - "mingcute:file-forbid-fill", - "mingcute:file-import-fill", "mingcute:file-info-fill", "mingcute:file-locked-fill", - "mingcute:file-more-fill", - "mingcute:file-new-fill", "mingcute:file-search-fill", - "mingcute:file-security-fill", - "mingcute:file-star-fill", - "mingcute:file-unknown-fill", - "mingcute:file-upload-fill", "mingcute:file-warning-fill", "mingcute:file-zip-fill", - - // Books & Knowledge + "mingcute:pdf-fill", + "mingcute:markdown-fill", + "mingcute:xls-fill", + "mingcute:folder-fill", + "mingcute:folder-open-fill", + "mingcute:folders-fill", + "mingcute:folder-locked-fill", + "mingcute:book-fill", "mingcute:book-2-fill", "mingcute:book-3-fill", - "mingcute:book-4-fill", - "mingcute:book-5-fill", "mingcute:book-6-fill", + "mingcute:notebook-fill", + "mingcute:notebook-2-fill", + "mingcute:diary-fill", + "mingcute:news-fill", + "mingcute:paper-fill", + "fluent:script-24-filled", + "mingcute:clipboard-fill", + "mingcute:list-check-fill", + "mingcute:list-ordered-fill", + "ph:kanban-fill", + "fluent:steps-24-filled", + "mingcute:task-fill", + "mingcute:task-2-fill", + "mingcute:to-do-fill", + "mingcute:report-fill", + "mingcute:report-forms-fill", + "mingcute:presentation-1-fill", + "mingcute:presentation-2-fill", + "mingcute:whiteboard-fill", + "mingcute:black-board-fill", + "mingcute:mind-map-fill", + "mingcute:sitemap-fill", + "mingcute:timeline-fill", + "mingcute:version-fill", + "mingcute:quill-pen-fill", + "mingcute:pen-fill", + "mingcute:pencil-fill", + "mingcute:edit-fill", + "mingcute:mark-pen-fill", + "mingcute:signature-fill", + "mingcute:certificate-fill", + "mingcute:seal-fill", + "mingcute:quote-left-fill", + "mingcute:translate-fill", + "mingcute:pin-fill", + "mingcute:inbox-fill", + "mingcute:archive-fill", + "mingcute:package-fill", + "mingcute:package-2-fill", + "mingcute:box-2-fill", + "mingcute:box-3-fill", + "mingcute:question-fill", + "mingcute:information-fill", + "mingcute:bulb-fill", + "mingcute:brain-fill", + "mingcute:thought-fill", + "mingcute:mortarboard-fill", + "mingcute:school-fill", + "mingcute:telescope-fill", + "mingcute:microscope-fill", + "mingcute:flask-fill", + "mingcute:test-tube-fill", + "mingcute:science-fill", + "mingcute:formula-fill", + "ph:sigma-fill", + "ph:math-operations-fill", + "ri:calculator-fill", + "ri:atom-fill", + "ri:dna-fill", + "mingcute:omega-fill", + "mingcute:asterisk-fill", - // Text & Writing - "mingcute:text-2-fill", - "mingcute:text-area-fill", - "mingcute:text-color-fill", - "mingcute:pen-2-fill", - "mingcute:paper-2-fill", - - // Git & Development + // code + "mingcute:code-fill", + "mingcute:braces-fill", + ["fluent:braces-variable-24-filled", "variable"], + "ph:function-fill", + "ph:binary-fill", + "mingcute:brackets-fill", + "mingcute:brackets-angle-fill", + "mingcute:markup-fill", + "mingcute:terminal-fill", + "mingcute:terminal-box-fill", + ["fluent:window-dev-tools-24-filled", "dev tools"], + "mingcute:command-fill", + "mingcute:hotkey-fill", + "mingcute:keyboard-fill", + "mingcute:bug-fill", "mingcute:git-branch-fill", "mingcute:git-commit-fill", "mingcute:git-compare-fill", - "mingcute:git-lab-fill", "mingcute:git-merge-fill", "mingcute:git-pull-request-fill", "mingcute:git-pull-request-close-fill", - "mingcute:terminal-box-fill", + "ri:git-repository-fill", + "ri:git-repository-private-fill", + "mingcute:plugin-fill", + "mingcute:plugin-2-fill", + "ph:plugs-connected-fill", + "mingcute:components-fill", + "mingcute:layers-fill", + "ph:stack-fill", + "mingcute:layer-fill", + "mingcute:cube-fill", + "mingcute:cube-3d-fill", + "mingcute:hexagon-fill", + "mingcute:hexagons-fill", + "mingcute:process-fill", + "fluent:pipeline-24-filled", + "fluent:flowchart-24-filled", + "ph:queue-fill", + "ri:infinity-fill", + "mingcute:transformation-fill", + "mingcute:random-fill", + "mingcute:repeat-fill", + "mingcute:shuffle-fill", + "mingcute:switch-fill", + "mingcute:toggle-right-fill", + "mingcute:cursor-fill", + "mingcute:mouse-fill", + "mingcute:inspect-fill", + "mingcute:search-fill", + "mingcute:filter-fill", + "mingcute:filter-2-fill", + "mingcute:sort-ascending-fill", + "mingcute:sort-descending-fill", + "mingcute:target-fill", + "mingcute:aiming-fill", + "mingcute:magic-1-fill", + "mingcute:sparkles-fill", + "mingcute:ai-fill", + "mingcute:robot-fill", + "mingcute:webhook-fill", + "mingcute:mqtt-fill", + "mingcute:rss-fill", + "mingcute:web-fill", + "mingcute:world-fill", + "mingcute:earth-fill", + "mingcute:globe-fill", + "mingcute:link-fill", + "mingcute:unlink-fill", + "mingcute:external-link-fill", + "mingcute:qrcode-fill", + "mingcute:barcode-fill", + "mingcute:scan-fill", + + // infrastructure + "mingcute:server-fill", "mingcute:server-2-fill", - - // Navigation & UI + "ri:instance-fill", + ["ph:shipping-container-fill", "container"], + "mingcute:storage-fill", + "ri:database-2-fill", + "ri:hard-drive-2-fill", + "mingcute:drive-fill", + "mingcute:cylinder-fill", + "mingcute:cylinder-2-fill", + "mingcute:safe-box-fill", + "mingcute:usb-fill", + "mingcute:usb-flash-disk-fill", + "mingcute:router-modem-fill", + "mingcute:base-station-fill", + "ph:network-fill", + "ph:broadcast-fill", + "mingcute:wifi-fill", + "mingcute:signal-fill", + "mingcute:bluetooth-fill", + "mingcute:nfc-fill", + "mingcute:cloud-fill", + "mingcute:cloud-2-fill", + "mingcute:clouds-fill", + "mingcute:cloud-lightning-fill", + "fluent:cloud-sync-24-filled", + "mingcute:monitor-fill", + "mingcute:display-fill", + "mingcute:computer-fill", + "mingcute:laptop-fill", + "mingcute:imac-fill", + "mingcute:device-fill", + "mingcute:chip-fill", + "ph:cpu-fill", + "ph:memory-fill", + "ri:ram-fill", + "ph:circuitry-fill", + "mingcute:cellphone-fill", + "mingcute:pad-fill", + "mingcute:watch-fill", + "mingcute:tv-1-fill", + "mingcute:projector-fill", + "mingcute:print-fill", + "mingcute:remote-fill", + "mingcute:power-fill", + "mingcute:flash-fill", + "mingcute:lightning-fill", + "mingcute:battery-fill", + "mingcute:battery-charging-fill", + "mingcute:charging-pile-fill", + "mingcute:solar-panel-fill", + "mingcute:high-voltage-power-fill", + "mingcute:engine-fill", + "mingcute:fan-fill", + "mingcute:thermometer-fill", + "mingcute:fire-fill", + "mingcute:flame-fill", + "mingcute:drop-fill", + "mingcute:wind-fill", + "mingcute:tornado-fill", + "mingcute:thunderstorm-fill", + "mingcute:sun-fill", + "mingcute:moon-fill", + "mingcute:moon-stars-fill", + "mingcute:planet-fill", + "mingcute:rocket-fill", + "mingcute:rocket-2-fill", + "mingcute:ufo-fill", + "mingcute:drone-fill", + "mingcute:radar-fill", + "mingcute:radar-2-fill", + "mingcute:compass-fill", + "mingcute:navigation-fill", + "mingcute:map-fill", + "mingcute:map-pin-fill", + "mingcute:location-fill", + "mingcute:route-fill", + "mingcute:directions-fill", + "mingcute:road-fill", + "mingcute:bridge-fill", + "mingcute:tunnel-fill", + "mingcute:tower-fill", + "mingcute:tv-tower-fill", + "mingcute:lighthouse-fill", + "mingcute:anchor-fill", + "mingcute:ship-fill", + "mingcute:sailboat-fill", + "mingcute:truck-fill", + "mingcute:train-fill", + "mingcute:bus-fill", + "mingcute:car-fill", + "mingcute:airplane-fill", + "mingcute:flight-takeoff-fill", + "mingcute:flight-land-fill", + "mingcute:parachute-fill", + "mingcute:lifebuoy-fill", + "mingcute:traffic-cone-fill", + "mingcute:traffic-lights-fill", "mingcute:home-1-fill", - "mingcute:home-2-fill", - "mingcute:home-3-fill", - "mingcute:home-4-fill", - "mingcute:home-5-fill", - "mingcute:home-6-fill", - "mingcute:home-7-fill", - "mingcute:arrow-up-circle-fill", - "mingcute:arrow-down-circle-fill", - "mingcute:arrow-left-circle-fill", - "mingcute:arrow-right-circle-fill", - - // Communication & Social - "mingcute:mail-fill", - "mingcute:mail-open-fill", - "mingcute:mail-send-fill", - "mingcute:chat-1-fill", - "mingcute:chat-2-fill", - "mingcute:chat-3-fill", - "mingcute:chat-4-fill", - "mingcute:message-1-fill", - "mingcute:message-2-fill", - "mingcute:message-3-fill", - "mingcute:message-4-fill", - "mingcute:comment-fill", - "mingcute:comment-2-fill", - "mingcute:notification-fill", - - // Users & Teams - "mingcute:user-1-fill", - "mingcute:user-2-fill", - "mingcute:user-3-fill", - "mingcute:user-4-fill", - "mingcute:group-fill", - "mingcute:group-2-fill", - "mingcute:group-3-fill", - - // Settings & Tools - "mingcute:settings-1-fill", - "mingcute:settings-2-fill", - "mingcute:settings-3-fill", - "mingcute:settings-4-fill", - "mingcute:settings-5-fill", - "mingcute:settings-6-fill", - "mingcute:settings-7-fill", + "mingcute:house-fill", + "mingcute:door-fill", + "mingcute:enter-door-fill", + "mingcute:exit-door-fill", + "mingcute:building-1-fill", + "mingcute:building-2-fill", + "mingcute:factory-fill", + "mingcute:bank-fill", + "mingcute:government-fill", + "mingcute:store-fill", + "mingcute:inventory-fill", + "mingcute:trunk-fill", + "mingcute:tower-crane-fill", + "mingcute:ladder-fill", + "mingcute:hammer-fill", + "mingcute:screwdriver-fill", "mingcute:tool-fill", + "ph:toolbox-fill", + "mingcute:settings-1-fill", + "mingcute:settings-3-fill", + "mingcute:settings-6-fill", + "mingcute:pickax-fill", + "mingcute:axe-fill", + "mingcute:shovel-fill", + "mingcute:magnet-fill", + "mingcute:broom-fill", + "mingcute:recycle-fill", + "mingcute:wastebasket-fill", + "mingcute:delete-2-fill", - // Stars & Favorites - "mingcute:star-fill", - "mingcute:star-half-fill", - "mingcute:heart-fill", - "mingcute:bookmark-fill", + // metrics & data + "mingcute:chart-bar-fill", + "mingcute:chart-bar-2-fill", + "mingcute:chart-line-fill", + "mingcute:chart-pie-fill", + "mingcute:chart-pie-2-fill", + "mingcute:chart-vertical-fill", + "mingcute:chart-horizontal-fill", + "mingcute:chart-decrease-fill", + ["fluent:data-scatter-24-filled", "scatter chart"], + ["fluent:data-treemap-24-filled", "treemap"], + "ph:graph-fill", + "mingcute:trending-up-fill", + "mingcute:trending-down-fill", + "mingcute:stock-fill", + "mingcute:performance-fill", + "mingcute:dashboard-fill", + "mingcute:dashboard-2-fill", + "mingcute:dashboard-3-fill", + "mingcute:dashboard-4-fill", + "ph:gauge-fill", + "mingcute:counter-fill", + "mingcute:percentage-fill", + "mingcute:wave-fill", + "mingcute:sound-line-fill", + "mingcute:heartbeat-fill", + "mingcute:heartbeat-2-fill", + "mingcute:table-fill", + "mingcute:table-2-fill", + "mingcute:grid-fill", + "mingcute:layout-fill", + "mingcute:balance-fill", + "mingcute:scale-fill", + "mingcute:ruler-fill", + "mingcute:pencil-ruler-fill", + "mingcute:gradienter-fill", + "mingcute:time-fill", + "mingcute:clock-fill", + "mingcute:stopwatch-fill", + "mingcute:sandglass-fill", + "mingcute:hours-fill", + "mingcute:history-fill", + "mingcute:schedule-fill", + "mingcute:calendar-fill", + "mingcute:calendar-month-fill", + "mingcute:calendar-week-fill", + "mingcute:alarm-1-fill", + + // security + "mingcute:key-1-fill", + "mingcute:key-2-fill", + "mingcute:keyhole-fill", + "fluent:password-24-filled", + "mingcute:lock-fill", + "mingcute:unlock-fill", + "mingcute:shield-fill", + "mingcute:shield-shape-fill", + "mingcute:safe-shield-fill", + "mingcute:safety-certificate-fill", + "mingcute:safe-lock-fill", + "mingcute:safe-alert-fill", + "mingcute:fingerprint-fill", + "mingcute:faceid-fill", + "mingcute:incognito-mode-fill", + "mingcute:eye-fill", + "mingcute:eye-close-fill", + "mingcute:virus-fill", + "mingcute:bomb-fill", + "mingcute:skull-fill", + "mingcute:ghost-fill", + "mingcute:sword-fill", + + // status & incidents + "mingcute:notification-fill", + "mingcute:bell-ringing-fill", + "mingcute:announcement-fill", + "mingcute:horn-fill", "mingcute:flag-1-fill", "mingcute:flag-2-fill", "mingcute:flag-3-fill", "mingcute:flag-4-fill", + "mingcute:check-circle-fill", + "mingcute:close-circle-fill", + "mingcute:forbid-circle-fill", + "mingcute:minus-circle-fill", + "mingcute:add-circle-fill", + "mingcute:alert-fill", + "mingcute:alert-octagon-fill", + "mingcute:alert-diamond-fill", + "mingcute:warning-fill", + "mingcute:fault-fill", + "mingcute:stethoscope-fill", + "mingcute:first-aid-kit-fill", + "mingcute:capsule-fill", + "mingcute:hospital-fill", + "mingcute:medal-fill", + "mingcute:trophy-fill", + "mingcute:award-fill", + "mingcute:star-fill", + "mingcute:heart-fill", + "mingcute:thumb-up-fill", + "mingcute:thumb-down-fill", - // Time & Calendar - "mingcute:calendar-fill", - "mingcute:calendar-2-fill", - "mingcute:calendar-3-fill", - "mingcute:calendar-add-fill", - "mingcute:calendar-day-fill", - "mingcute:calendar-month-fill", - "mingcute:calendar-time-add-fill", - "mingcute:calendar-week-fill", - "mingcute:time-fill", - "mingcute:time-duration-fill", - "mingcute:alarm-1-fill", - "mingcute:alarm-2-fill", - "mingcute:history-fill", - "mingcute:history-anticlockwise-fill", - - // Media + // people & communication + "mingcute:user-1-fill", + "mingcute:user-3-fill", + "mingcute:user-add-fill", + "mingcute:user-setting-fill", + "mingcute:user-security-fill", + "mingcute:user-lock-fill", + "mingcute:user-star-fill", + "mingcute:user-search-fill", + "mingcute:group-fill", + "mingcute:group-2-fill", + "mingcute:contacts-fill", + "mingcute:department-fill", + "mingcute:idcard-fill", + "mingcute:profile-fill", + "mingcute:invite-fill", + "mingcute:briefcase-fill", + "mingcute:suitcase-fill", + "mingcute:mail-fill", + "mingcute:mail-send-fill", + "mingcute:mail-open-fill", + "mingcute:chat-1-fill", + "mingcute:chat-2-fill", + "mingcute:chat-4-fill", + "mingcute:message-1-fill", + "mingcute:message-3-fill", + "mingcute:comment-fill", + "mingcute:phone-fill", + "mingcute:phone-call-fill", "mingcute:video-fill", "mingcute:video-camera-fill", - "mingcute:music-fill", - "mingcute:music-2-fill", - "mingcute:volume-fill", - "mingcute:volume-mute-fill", "mingcute:mic-fill", - "mingcute:mic-2-fill", "mingcute:headphone-fill", + "mingcute:speaker-fill", + "mingcute:volume-fill", + "mingcute:live-fill", + "mingcute:hands-clapping-fill", + "mingcute:wave-hand-fill", + "mingcute:hand-heart-fill", + "ph:handshake-fill", + "mingcute:celebrate-fill", + "mingcute:firework-fill", + "mingcute:gift-fill", + "mingcute:cake-fill", + "mingcute:teacup-fill", + "mingcute:game-1-fill", + "mingcute:chess-fill", + "mingcute:leaf-fill", + "mingcute:tree-2-fill", - // Security & Lock - "mingcute:lock-fill", - "mingcute:unlock-fill", - "mingcute:shield-fill", - "mingcute:key-1-fill", - "mingcute:key-2-fill", + // money + "mingcute:wallet-fill", + "mingcute:coin-fill", + "mingcute:cash-fill", + "mingcute:bank-card-fill", + "mingcute:bill-fill", + "mingcute:pig-money-fill", + "mingcute:currency-dollar-fill", + "mingcute:currency-euro-fill", + "mingcute:receive-money-fill", + "mingcute:exchange-dollar-fill", + "mingcute:sale-fill", + "mingcute:coupon-fill", + "mingcute:ticket-fill", + "mingcute:auction-fill", - // Data & Charts - "mingcute:chart-bar-fill", - "mingcute:chart-decrease-fill", - "mingcute:chart-line-fill", - "mingcute:chart-pie-fill", - "mingcute:chart-pie-2-fill", - "mingcute:dashboard-2-fill", - "mingcute:dashboard-3-fill", - "mingcute:dashboard-4-fill", + // brands MingCute draws in its own style + "mingcute:github-fill", + "mingcute:git-lab-fill", + "mingcute:linux-fill", + "mingcute:ubuntu-fill", + "mingcute:windows-fill", + "mingcute:apple-fill", + "mingcute:android-fill", + "mingcute:chrome-fill", + "mingcute:firefox-fill", + "mingcute:safari-fill", + "mingcute:edge-fill", + "mingcute:vscode-fill", + "mingcute:react-fill", + "mingcute:vue-fill", + "mingcute:figma-fill", + "mingcute:notion-fill", + "mingcute:discord-fill", + "mingcute:telegram-fill", + "mingcute:dropbox-fill", + "mingcute:stripe-fill", + "mingcute:paypal-fill", + "mingcute:youtube-fill", + "mingcute:reddit-fill", + "mingcute:mastodon-fill", + "mingcute:openai-fill", + "mingcute:claude-fill", + "mingcute:copilot-fill", + "mingcute:mcp-fill", + "mingcute:deepseek-fill", + "mingcute:gemini-fill", + "mingcute:grok-fill", + "mingcute:perplexity-fill", + "mingcute:vibe-coding-fill", - // Cloud & Storage - "mingcute:cloud-fill", - "mingcute:cloud-2-fill", - "mingcute:save-2-fill", - "mingcute:save-fill", - "mingcute:archive-fill", + // containers & clusters + "simple-icons:kubernetes", + "simple-icons:docker", + "simple-icons:containerd", + "simple-icons:helm", + "simple-icons:rancher", + "simple-icons:k3s", + "simple-icons:talos", + ["simple-icons:redhatopenshift", "openshift"], + "simple-icons:istio", + "simple-icons:linkerd", + ["simple-icons:envoyproxy", "envoy"], + "simple-icons:argo", + "simple-icons:openstack", + "simple-icons:proxmox", + "file-icons:vmware", + "simple-icons:virtualbox", + "simple-icons:qemu", + "simple-icons:portainer", - // Location - "mingcute:location-fill", - "mingcute:location-2-fill", - "mingcute:map-fill", - "mingcute:map-2-fill", - "mingcute:compass-fill", - "mingcute:globe-fill", - "mingcute:globe-2-fill", + // infrastructure as code + "simple-icons:terraform", + "simple-icons:ansible", + "simple-icons:puppet", + "simple-icons:chef", + "simple-icons:pulumi", + "simple-icons:hashicorp", + "simple-icons:vault", + "simple-icons:consul", + "simple-icons:nomad", + "simple-icons:backstage", - // Editing - "mingcute:edit-fill", - "mingcute:edit-2-fill", - "mingcute:edit-3-fill", - "mingcute:edit-4-fill", - "mingcute:scissors-fill", - "mingcute:scissors-2-fill", - "mingcute:copy-fill", - "mingcute:copy-2-fill", - "mingcute:copy-3-fill", - "mingcute:clipboard-fill", - "mingcute:refresh-1-fill", - "mingcute:refresh-2-fill", - "mingcute:refresh-3-fill", - "mingcute:refresh-4-fill", + // observability + "simple-icons:prometheus", + "simple-icons:grafana", + "simple-icons:opentelemetry", + "simple-icons:jaeger", + "simple-icons:thanos", + "simple-icons:victoriametrics", + "simple-icons:influxdb", + "simple-icons:elasticsearch", + "simple-icons:kibana", + "simple-icons:opensearch", + "simple-icons:sentry", + "simple-icons:datadog", + ["simple-icons:newrelic", "new relic"], + "simple-icons:pagerduty", + ["simple-icons:uptimekuma", "uptime kuma"], - // Status & Info - "mingcute:information-fill", - "mingcute:question-fill", - "mingcute:alert-fill", - "mingcute:warning-fill", - "mingcute:checkbox-fill", - "mingcute:radio-fill", + // data stores & pipelines + "devicon-plain:postgresql", + "devicon-plain:mysql", + "simple-icons:mariadb", + "simple-icons:sqlite", + "simple-icons:mongodb", + "simple-icons:redis", + "simple-icons:clickhouse", + ["simple-icons:cockroachlabs", "cockroachdb"], + "simple-icons:etcd", + "simple-icons:meilisearch", + "simple-icons:minio", + "simple-icons:rabbitmq", + ["simple-icons:apachekafka", "kafka"], + "cib:cassandra", + ["cib:apache-spark", "spark"], + ["simple-icons:snowflake", "snowflake (data cloud)"], + "simple-icons:databricks", + "simple-icons:supabase", + "ri:firebase-fill", + "simple-icons:prisma", + "simple-icons:metabase", + "simple-icons:posthog", + ["simple-icons:googleanalytics", "google analytics"], - // Shapes & Icons - "mingcute:cube-fill", - "mingcute:cube-3d-fill", - "mingcute:box-2-fill", - "mingcute:box-3-fill", - "mingcute:hexagon-fill", - "mingcute:pentagon-fill", - "mingcute:square-fill", - "mingcute:triangle-fill", + // networking & edge + "simple-icons:nginx", + "file-icons:caddy", + "simple-icons:kong", + "simple-icons:cloudflare", + ["simple-icons:letsencrypt", "let's encrypt"], + "simple-icons:wireguard", + "simple-icons:openvpn", + "simple-icons:tailscale", - // Power & System - "mingcute:power-fill", - "mingcute:flash-fill", - "mingcute:bulb-fill", - "mingcute:bulb-2-fill", - "mingcute:battery-fill", - "mingcute:battery-2-fill", - "mingcute:battery-charging-fill", - "mingcute:wifi-fill", - "mingcute:signal-fill", - "mingcute:bluetooth-fill", + // source control & delivery + "simple-icons:git", + "simple-icons:gitea", + "simple-icons:bitbucket", + "devicon-plain:jenkins", + "simple-icons:circleci", + "simple-icons:dependabot", + "simple-icons:codecov", + "simple-icons:snyk", + "simple-icons:trivy", + "simple-icons:owasp", - // Tags & Labels - "mingcute:tag-fill", - "mingcute:tag-2-fill", - "mingcute:hashtag-fill", - "mingcute:at-fill", + // cloud + ["simple-icons:amazonwebservices", "aws"], + ["simple-icons:googlecloud", "google cloud"], + ["simple-icons:googlebigquery", "bigquery"], + ["simple-icons:microsoftazure", "azure"], + ["simple-icons:azuredevops", "azure devops"], + "simple-icons:digitalocean", + "simple-icons:hetzner", + "simple-icons:vercel", + "cib:netlify", + "simple-icons:heroku", + ["simple-icons:flydotio", "fly.io"], + "simple-icons:railway", + + // operating systems & devices + "simple-icons:fedora", + "mdi:debian", + ["simple-icons:archlinux", "arch linux"], + ["simple-icons:alpinelinux", "alpine linux"], + "simple-icons:nixos", + ["simple-icons:rockylinux", "rocky linux"], + "ri:centos-fill", + ["simple-icons:redhat", "red hat"], + "simple-icons:freebsd", + ["simple-icons:raspberrypi", "raspberry pi"], + ["simple-icons:homeassistant", "home assistant"], + ["simple-icons:eclipsemosquitto", "mosquitto"], + "simple-icons:zapier", + + // languages & shells + "devicon-plain:go", + "simple-icons:rust", + "simple-icons:python", + "simple-icons:typescript", + "simple-icons:javascript", + ["devicon-plain:nodejs", "node.js"], + "simple-icons:bun", + "simple-icons:deno", + "ri:php-fill", + "simple-icons:ruby", + "simple-icons:kotlin", + "simple-icons:swift", + ["devicon-plain:dot-net", ".net"], + ["simple-icons:csharp", "c#"], + ["simple-icons:cplusplus", "c++"], + "simple-icons:c", + "simple-icons:zig", + "simple-icons:elixir", + "simple-icons:erlang", + "simple-icons:haskell", + "simple-icons:scala", + "simple-icons:lua", + "devicon-plain:perl", + "simple-icons:r", + "simple-icons:julia", + "simple-icons:webassembly", + "simple-icons:powershell", + "mdi:bash", + + // frameworks & tooling + ["simple-icons:vuedotjs", "vue.js"], + "simple-icons:nuxt", + ["simple-icons:nextdotjs", "next.js"], + "simple-icons:svelte", + "simple-icons:angular", + "simple-icons:astro", + "simple-icons:hono", + "simple-icons:nestjs", + "simple-icons:fastapi", + "simple-icons:django", + ["simple-icons:flask", "flask (python)"], + "simple-icons:spring", + "devicon-plain:laravel", + ["devicon-plain:rails", "ruby on rails"], + ["simple-icons:tailwindcss", "tailwind css"], + "simple-icons:vite", + "simple-icons:webpack", + "simple-icons:eslint", + "simple-icons:prettier", + "simple-icons:vitest", + "simple-icons:jest", + "simple-icons:playwright", + "simple-icons:cypress", + "simple-icons:storybook", + "simple-icons:graphql", + ["simple-icons:openapiinitiative", "openapi"], + "simple-icons:swagger", + "simple-icons:postman", + "simple-icons:npm", + "simple-icons:pnpm", + "simple-icons:yarn", + ["simple-icons:d3dotjs", "d3.js"], + "simple-icons:mermaid", + "simple-icons:excalidraw", + ["simple-icons:diagramsdotnet", "diagrams.net"], + "simple-icons:json", + "simple-icons:yaml", + + // ai + "simple-icons:anthropic", + ["simple-icons:huggingface", "hugging face"], + "simple-icons:pytorch", + "simple-icons:tensorflow", + "simple-icons:ollama", + "simple-icons:langchain", + ["simple-icons:mistralai", "mistral"], + ["simple-icons:cursor", "cursor (editor)"], + + // collaboration & docs + "simple-icons:slack", + "simple-icons:jira", + "simple-icons:confluence", + "simple-icons:trello", + "simple-icons:linear", + "simple-icons:clickup", + "simple-icons:asana", + "simple-icons:miro", + "simple-icons:mattermost", + ["simple-icons:googlemeet", "google meet"], + ["simple-icons:microsoftteams", "microsoft teams"], + ["ph:matrix-logo-fill", "matrix"], + "simple-icons:obsidian", + ["simple-icons:readthedocs", "read the docs"], + "simple-icons:docusaurus", + "simple-icons:gitbook", + ["simple-icons:stackoverflow", "stack overflow"], + + // editors & terminals + "simple-icons:jetbrains", + "simple-icons:neovim", + "simple-icons:vim", + "simple-icons:tmux", + + // identity & security tooling + "simple-icons:1password", + "simple-icons:bitwarden", + "simple-icons:keycloak", + "simple-icons:auth0", + "simple-icons:okta", + "simple-icons:authelia", + "simple-icons:authentik", + "simple-icons:wireshark", + "simple-icons:metasploit", + ["simple-icons:burpsuite", "burp suite"], + + // storage & self-hosting + "simple-icons:rclone", + "simple-icons:truenas", + + // third-party services + "simple-icons:twilio", + "simple-icons:sendgrid", ] -// these icons aren't scanned and bundled automatically -export const selectableIcons: { - normalName: string // without prefix - prettyName: string - id: string -}[] = [] +// the icon's own name without its collection's style suffix. A trailing +// index is a collection's disambiguator between variants of one concept; +// the glyph tells them apart, so the name does not +function displayName(id: string) { + return id + .slice(id.indexOf(":") + 1) + .replace(/-24-filled$|-fill$/, "") + .replace(/-\d+$/, "") + .replaceAll("-", " ") +} -mingCuteIcons.forEach((k) => { - const name = k.replace("mingcute:", "") - selectableIcons.push({ - normalName: name, - prettyName: name.replace(/-fill$/, "").replaceAll("-", " "), - id: k, - }) -}) +export const selectableIcons: { + id: string + name: string // displayed and searched +}[] = icons.map((entry) => + typeof entry === "string" + ? { id: entry, name: displayName(entry) } + : { id: entry[0], name: entry[1] }, +) export function selectableIconList() { return selectableIcons.map((v) => { diff --git a/web/nuxt.config.ts b/web/nuxt.config.ts index 0c500bfe..323a5126 100644 --- a/web/nuxt.config.ts +++ b/web/nuxt.config.ts @@ -176,7 +176,12 @@ export default defineNuxtConfig({ mode: "css", cssLayer: "base", clientBundle: { - icons: selectableIconList(), + // the document title icons are a fifth of the startup download, + // so the web build leaves them out and the server resolves each + // one on first use: inlined for SSR, fetched in batches after + // client-side navigation. The desktop build has no server to ask + // and loads from disk, so it bundles them and stays offline-safe + icons: isDesktopBuild ? selectableIconList() : [], scan: true, sizeLimitKb: 256, }, diff --git a/web/package.json b/web/package.json index 009a6d16..b3eb9bd9 100644 --- a/web/package.json +++ b/web/package.json @@ -117,11 +117,18 @@ "@electron-forge/plugin-vite": "^7.11.1", "@electron/fuses": "^1.8.0", "@iconify-json/basil": "^1.2.4", + "@iconify-json/cib": "^1.2.3", "@iconify-json/devicon": "^1.2.56", + "@iconify-json/devicon-plain": "^1.2.57", + "@iconify-json/file-icons": "^1.2.2", + "@iconify-json/fluent": "^1.2.57", "@iconify-json/hugeicons": "^1.2.33", "@iconify-json/iconoir": "^1.2.7", "@iconify-json/lucide": "^1.2.126", + "@iconify-json/mdi": "^1.2.3", "@iconify-json/mingcute": "^1.2.8", + "@iconify-json/ph": "^1.2.2", + "@iconify-json/ri": "^1.2.10", "@iconify-json/simple-icons": "^1.2.93", "@iconify-json/svg-spinners": "^1.2.4", "@intlify/eslint-plugin-vue-i18n": "^4.5.1", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 5cf75dcc..8d81ff77 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -234,9 +234,21 @@ importers: '@iconify-json/basil': specifier: ^1.2.4 version: 1.2.4 + '@iconify-json/cib': + specifier: ^1.2.3 + version: 1.2.3 '@iconify-json/devicon': specifier: ^1.2.56 version: 1.2.62 + '@iconify-json/devicon-plain': + specifier: ^1.2.57 + version: 1.2.57 + '@iconify-json/file-icons': + specifier: ^1.2.2 + version: 1.2.2 + '@iconify-json/fluent': + specifier: ^1.2.57 + version: 1.2.57 '@iconify-json/hugeicons': specifier: ^1.2.33 version: 1.2.33 @@ -246,9 +258,18 @@ importers: '@iconify-json/lucide': specifier: ^1.2.126 version: 1.2.126 + '@iconify-json/mdi': + specifier: ^1.2.3 + version: 1.2.3 '@iconify-json/mingcute': specifier: ^1.2.8 version: 1.2.8 + '@iconify-json/ph': + specifier: ^1.2.2 + version: 1.2.2 + '@iconify-json/ri': + specifier: ^1.2.10 + version: 1.2.10 '@iconify-json/simple-icons': specifier: ^1.2.93 version: 1.2.93 @@ -1438,9 +1459,21 @@ packages: '@iconify-json/basil@1.2.4': resolution: {integrity: sha512-KUL5JBdFDF7APY8XiuqvfpiecIby8pIbs6rm3WLgdVDqMlKH0WcWEwd+ilANby8X3AuHQVrjNjByh9MpfAR3lQ==} + '@iconify-json/cib@1.2.3': + resolution: {integrity: sha512-MMvRXVTHt82z3bYN19JDAjv/X8OBNC/1B3I334SV/1nyhdPeeyvJZVoI1cGuNqYznBjMnWxKPtIhhiVgGQ1CzQ==} + + '@iconify-json/devicon-plain@1.2.57': + resolution: {integrity: sha512-3p8PgK63KejM1DhuL7LBslvfmPmwjx6DkO3EM+om6MGc8kpc9yX7CgOPWuM2sFSxEblzqxAwnbUlIO56fJjMRw==} + '@iconify-json/devicon@1.2.62': resolution: {integrity: sha512-X0o0+fOJL2t5JTbDCGLRzrmvf8Pv5q8dr86+fW+F/VJ9qvXTtny6lqGIwjE9AY4VTLm3i8f/N+VtRheSW6OkEg==} + '@iconify-json/file-icons@1.2.2': + resolution: {integrity: sha512-ajk44wYGTiu79EAyrfNHNaxZ1/2Z1xuSOAvYUT/pAPWHc+P6n9TUZP/ccnPG18kx0WMBxmkHHQ9/zkm6ENhk+Q==} + + '@iconify-json/fluent@1.2.57': + resolution: {integrity: sha512-ecYjdYhwiQ+UNz94cOnYY8RCRbZ8+N54jzocxmfgRZgblM2IvG8RRGeI9Z52jxG8KhZcLftxyhvvk/rrqKSZLw==} + '@iconify-json/hugeicons@1.2.33': resolution: {integrity: sha512-x5a5/Ipd+v9c9qSisecCR+KMAegZuWDoVeZfp5RREdzOP9/Orec7w0BNUVZrkNOEt5oVtttCu/DXb5etc8Sfig==} @@ -1450,9 +1483,18 @@ packages: '@iconify-json/lucide@1.2.126': resolution: {integrity: sha512-Fl3OfR71yeWLrlTLp6C4W5W3rJJDWH9/e70mjtq9VAldYDxvHh149JMNPz7foTeLLTE2Paynnp2aYKVL9rgF5Q==} + '@iconify-json/mdi@1.2.3': + resolution: {integrity: sha512-O3cLwbDOK7NNDf2ihaQOH5F9JglnulNDFV7WprU2dSoZu3h3cWH//h74uQAB87brHmvFVxIOkuBX2sZSzYhScg==} + '@iconify-json/mingcute@1.2.8': resolution: {integrity: sha512-J8VXpRwTpx5yzOzyhx3T4IXnfXIKTWFl3rZ9okcW4c+NMMuwkLvdmM4rkjB83tnYIhg5+kkeMpmw8tvSnxd4rQ==} + '@iconify-json/ph@1.2.2': + resolution: {integrity: sha512-PgkEZNtqa8hBGjHXQa4pMwZa93hmfu8FUSjs/nv4oUU6yLsgv+gh9nu28Kqi8Fz9CCVu4hj1MZs9/60J57IzFw==} + + '@iconify-json/ri@1.2.10': + resolution: {integrity: sha512-WWMhoncVVM+Xmu9T5fgu2lhYRrKTEWhKk3Com0KiM111EeEsRLiASjpsFKnC/SrB6covhUp95r2mH8tGxhgd5Q==} + '@iconify-json/simple-icons@1.2.93': resolution: {integrity: sha512-/XhANjfGYOuqvSR3TmUnkQkINvQ4GVjVuukvymRbxtVFBvIq/yiXJqCDycKcQPT401OYT9H2vIY6ihAlz1QIAw==} @@ -10449,10 +10491,26 @@ snapshots: dependencies: '@iconify/types': 2.0.0 + '@iconify-json/cib@1.2.3': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify-json/devicon-plain@1.2.57': + dependencies: + '@iconify/types': 2.0.0 + '@iconify-json/devicon@1.2.62': dependencies: '@iconify/types': 2.0.0 + '@iconify-json/file-icons@1.2.2': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify-json/fluent@1.2.57': + dependencies: + '@iconify/types': 2.0.0 + '@iconify-json/hugeicons@1.2.33': dependencies: '@iconify/types': 2.0.0 @@ -10465,10 +10523,22 @@ snapshots: dependencies: '@iconify/types': 2.0.0 + '@iconify-json/mdi@1.2.3': + dependencies: + '@iconify/types': 2.0.0 + '@iconify-json/mingcute@1.2.8': dependencies: '@iconify/types': 2.0.0 + '@iconify-json/ph@1.2.2': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify-json/ri@1.2.10': + dependencies: + '@iconify/types': 2.0.0 + '@iconify-json/simple-icons@1.2.93': dependencies: '@iconify/types': 2.0.0