a dock tile can be bare, so artwork is not framed in a swatch
The tile background exists so a white lucide glyph has something to sit on — on nothing it is invisible. Artwork does not need it, and a logo framed in an arbitrary coloured square reads as a mistake. badge rounded square filled with `color`, icon inset (what a glyph needs) bare no background, artwork fills the tile (what a mark wants) DEFAULTED from whether the plugin ships assets/icon.png — artwork gets bare, a glyph gets badge — with `tile` on the manifest to override either way. Presence is the declaration, as everywhere else here; the field exists only for the plugin whose artwork genuinely wants a backdrop. Music sets nothing and gets bare. `color` is still required either way. It does four jobs and only one of them is the square: the active glow, the indicator dot and the plugins-list tint all read it too. The icon was also smaller than it looked. The source carried ~8% transparent margin on every side and the artwork is 233x221 inside a square frame, so with the old `h-7 w-7` inset inside a `w-10` box the duck occupied roughly 39% of the tile's area. Cropped to its alpha bounding box, padded back to square to keep aspect, and rescaled: it now fills 95% of the frame instead of 79%, and `bare` gives it the whole tile instead of half. Measured rather than eyeballed — the bounding box came from extracting the alpha plane and scanning it, then the crop was applied to the 1254px original so nothing was resampled twice. tsgo clean, 797 tests, 787 pass, same 7. Verified live: music reports tile=bare, example and offscale still badge.
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 123 KiB |
@@ -11,6 +11,14 @@ export type DockItem = {
|
||||
// Either a lucide glyph (rendered white on the coloured tile) or an image asset (e.g. an app favicon).
|
||||
icon?: LucideIcon;
|
||||
image?: string;
|
||||
/**
|
||||
* `badge` (the default) fills a rounded square with `color` and insets the icon — which is what a
|
||||
* white lucide glyph needs, because on nothing it is invisible. `bare` draws no background and lets
|
||||
* artwork fill the tile, because a logo framed in an arbitrary swatch reads as a mistake.
|
||||
*
|
||||
* `color` is still used either way: the active glow and the indicator dot are drawn from it.
|
||||
*/
|
||||
tile?: 'badge' | 'bare';
|
||||
};
|
||||
|
||||
type DockProps = {
|
||||
@@ -101,12 +109,20 @@ export const Dock = ({ items, className, boundaryRef }: DockProps) => {
|
||||
<div
|
||||
className="w-10 h-10 md:w-12 md:h-12 rounded-xl flex items-center justify-center transition-all"
|
||||
style={{
|
||||
background: item.color,
|
||||
// `bare` keeps the box — it is what sizes the row and carries the glow — and simply
|
||||
// does not paint it.
|
||||
background: item.tile === 'bare' ? 'transparent' : item.color,
|
||||
boxShadow: isActive ? `0 0 12px ${item.color}40` : 'none',
|
||||
}}
|
||||
>
|
||||
{item.image ? (
|
||||
<img src={item.image} alt="" className="h-7 w-7 md:h-8 md:w-8 object-contain" />
|
||||
<img
|
||||
src={item.image}
|
||||
alt=""
|
||||
className={
|
||||
item.tile === 'bare' ? 'h-full w-full object-contain' : 'h-7 w-7 md:h-8 md:w-8 object-contain'
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
item.icon && <item.icon className="h-5 w-5 md:h-6 md:w-6 text-white" />
|
||||
)}
|
||||
@@ -182,15 +198,30 @@ export const CORE_DOCK_ITEMS: DockItem[] = [
|
||||
*/
|
||||
export function dockItemsFromPlugins(plugins: PluginManifest[]): DockItem[] {
|
||||
return plugins.flatMap((plugin) => {
|
||||
const tile = (t: { name: string; icon?: string; image?: string; color: string; route: string }): DockItem => ({
|
||||
const tile = (t: {
|
||||
name: string;
|
||||
icon?: string;
|
||||
image?: string;
|
||||
tile?: 'badge' | 'bare';
|
||||
color: string;
|
||||
route: string;
|
||||
}): DockItem => ({
|
||||
label: t.name,
|
||||
to: t.route,
|
||||
color: t.color,
|
||||
...(t.tile ? { tile: t.tile } : {}),
|
||||
...(t.image ? { image: t.image } : { icon: resolveIcon(t.icon ?? 'Box') }),
|
||||
});
|
||||
|
||||
return [
|
||||
tile({ name: plugin.name, icon: plugin.icon, image: plugin.image, color: plugin.color, route: plugin.rootRoute }),
|
||||
tile({
|
||||
name: plugin.name,
|
||||
icon: plugin.icon,
|
||||
image: plugin.image,
|
||||
tile: plugin.tile,
|
||||
color: plugin.color,
|
||||
route: plugin.rootRoute,
|
||||
}),
|
||||
...(plugin.extraTiles ?? []).map(tile),
|
||||
];
|
||||
});
|
||||
|
||||
@@ -102,8 +102,25 @@ export type PluginManifest = {
|
||||
* `assets/icon.png`. Optional for that reason: a plugin with its own artwork has nothing to say here.
|
||||
*/
|
||||
icon?: string;
|
||||
/** Tile colour. */
|
||||
/**
|
||||
* Accent colour. Four jobs, not one: the dock tile's background in `badge` style, the active glow,
|
||||
* the active indicator dot, and the tint on the plugins list. Required even for a plugin whose tile
|
||||
* draws no background, because the other three still use it.
|
||||
*/
|
||||
color: string;
|
||||
/**
|
||||
* How the dock draws this plugin's tile.
|
||||
*
|
||||
* badge a rounded square filled with `color`, the icon inset — what a lucide GLYPH needs, since a
|
||||
* white glyph on nothing is invisible.
|
||||
* bare no background; the artwork fills the tile — what a brand mark wants, since a logo framed
|
||||
* in an arbitrary swatch reads as a mistake.
|
||||
*
|
||||
* DEFAULTED from whether the plugin ships `assets/icon.png`: artwork gets `bare`, a glyph gets
|
||||
* `badge`. Presence is the declaration here as everywhere else, and this field only exists to
|
||||
* override that — a plugin whose artwork genuinely wants a coloured backdrop can say `badge`.
|
||||
*/
|
||||
tile?: 'badge' | 'bare';
|
||||
|
||||
permissions: PluginPermission[];
|
||||
|
||||
|
||||
@@ -184,6 +184,7 @@ export async function pluginDockManifests(): Promise<
|
||||
name: string;
|
||||
icon?: string;
|
||||
image?: string;
|
||||
tile?: 'badge' | 'bare';
|
||||
color: string;
|
||||
rootRoute: string;
|
||||
routes: string[];
|
||||
@@ -202,6 +203,8 @@ export async function pluginDockManifests(): Promise<
|
||||
// artwork of its own. The dock renders `image` as an <img> and `icon` through `resolveIcon`,
|
||||
// and it already preferred the image — this is the first thing to give it one.
|
||||
...(plugin.icon ? { image: iconUrl(plugin.appName) } : { icon: plugin.manifest.icon }),
|
||||
// Artwork defaults to `bare`, a glyph to `badge`; the manifest overrides either way.
|
||||
tile: plugin.manifest.tile ?? (plugin.icon ? 'bare' : 'badge'),
|
||||
color: plugin.manifest.color,
|
||||
rootRoute: prefix,
|
||||
routes: [prefix],
|
||||
|
||||
@@ -46,6 +46,8 @@ export type PluginManifest = {
|
||||
name: string;
|
||||
icon?: string;
|
||||
image?: string;
|
||||
/** `bare` draws no tile background — for a plugin whose icon is artwork rather than a glyph. */
|
||||
tile?: 'badge' | 'bare';
|
||||
color: string;
|
||||
rootRoute: string;
|
||||
routes: string[];
|
||||
|
||||
Reference in New Issue
Block a user