{"_id":"@emdzej/bimmerz-ui","_rev":"3-4ba6f39aa75dd4042026bfabaeef3735","name":"@emdzej/bimmerz-ui","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"@emdzej/bimmerz-ui","version":"0.1.0","_id":"@emdzej/bimmerz-ui@0.1.0","maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"homepage":"https://github.com/emdzej/bimmerz#readme","bugs":{"url":"https://github.com/emdzej/bimmerz/issues"},"dist":{"shasum":"3b3fc16b426be3dbb55bda913510ce4d371c9477","tarball":"https://registry.npmjs.org/@emdzej/bimmerz-ui/-/bimmerz-ui-0.1.0.tgz","fileCount":6,"integrity":"sha512-T3yiZTzE4VqmjMiNZSb/r1H6SqAOd5iI6G0PLpJpX435faAHcqHkSYN5E1+gMcd0Xl3ZApbXkDaY+Zsy/NMgfQ==","signatures":[{"sig":"MEQCIGOoxjIQlMI8Y9jFMNwC8ngIKIFz1Up35t6chDfYR4NOAiA2SE3TOs4q+gDy+dHYaGooGPrt1zsZDTrsirUjagotUw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":10605},"type":"module","_from":"file:emdzej-bimmerz-ui-0.1.0.tgz","types":"./src/index.ts","svelte":"./src/index.ts","exports":{".":{"types":"./src/index.ts","import":"./src/index.ts","svelte":"./src/index.ts"}},"scripts":{"test":"vitest run --passWithNoTests","build":"svelte-check --tsconfig ./tsconfig.json","clean":"rm -rf .turbo *.tsbuildinfo","typecheck":"svelte-check --tsconfig ./tsconfig.json"},"_npmUser":{"name":"emdzej","email":"michal@jaskolski.pro"},"_resolved":"/private/var/folders/7h/x_w_580x4s9dq3sq11tpvzkwy3nbj8/T/2e5b11dfbe3d25c3c0c59c5a0add2e7f/emdzej-bimmerz-ui-0.1.0.tgz","_integrity":"sha512-T3yiZTzE4VqmjMiNZSb/r1H6SqAOd5iI6G0PLpJpX435faAHcqHkSYN5E1+gMcd0Xl3ZApbXkDaY+Zsy/NMgfQ==","repository":{"url":"git+https://github.com/emdzej/bimmerz.git","type":"git","directory":"packages/ui"},"_npmVersion":"11.16.0","description":"Shared Svelte 5 components for the bimmerz app family — About dialog, Settings scaffolding, Connect button, brand wordmark, install picker. Source-only — consumer apps' Vite compiles them.","directories":{},"_nodeVersion":"22.22.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"svelte":"^5.0.0","typescript":"^5.6.0","svelte-check":"^4.0.0","@tsconfig/svelte":"^5.0.4"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/bimmerz-ui_0.1.0_1781257939654_0.7898053916429026","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@emdzej/bimmerz-ui","version":"0.2.0","_id":"@emdzej/bimmerz-ui@0.2.0","maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"homepage":"https://github.com/emdzej/bimmerz#readme","bugs":{"url":"https://github.com/emdzej/bimmerz/issues"},"dist":{"shasum":"56985f455c4fb45d041d12ba2012eee92b2641ee","tarball":"https://registry.npmjs.org/@emdzej/bimmerz-ui/-/bimmerz-ui-0.2.0.tgz","fileCount":7,"integrity":"sha512-KdRzkLVfQ1kbPyjb01Fl4dN7acE5qye2I85cBmbpiej44M27Kd0iwayIYJsFpGL+LoWbOaOzK3KMA53Mrn+t2g==","signatures":[{"sig":"MEYCIQCGZj3ttHy0yEZNDqwod5VkWPvokE2obMFfZFA5VMR1zAIhAPR/wbT86sV/2JQ4tJCezdhZyZ/d12clxtbjq3jml7SN","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21609},"type":"module","_from":"file:emdzej-bimmerz-ui-0.2.0.tgz","types":"./src/index.ts","svelte":"./src/index.ts","exports":{".":{"types":"./src/index.ts","import":"./src/index.ts","svelte":"./src/index.ts"}},"scripts":{"test":"vitest run --passWithNoTests","build":"svelte-check --tsconfig ./tsconfig.json","clean":"rm -rf .turbo *.tsbuildinfo","typecheck":"svelte-check --tsconfig ./tsconfig.json"},"_npmUser":{"name":"emdzej","email":"michal@jaskolski.pro"},"_resolved":"/private/var/folders/7h/x_w_580x4s9dq3sq11tpvzkwy3nbj8/T/8f5df96e194747ac849caf77f6363c28/emdzej-bimmerz-ui-0.2.0.tgz","_integrity":"sha512-KdRzkLVfQ1kbPyjb01Fl4dN7acE5qye2I85cBmbpiej44M27Kd0iwayIYJsFpGL+LoWbOaOzK3KMA53Mrn+t2g==","repository":{"url":"git+https://github.com/emdzej/bimmerz.git","type":"git","directory":"packages/ui"},"_npmVersion":"11.16.0","description":"Shared Svelte 5 components + lifecycle hooks for the bimmerz app family — brand wordmark, M-stripe, embedded-mode auto-connect hook. Source-only — consumer apps' Vite compiles them.","directories":{},"_nodeVersion":"22.22.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"svelte":"^5.0.0","typescript":"^5.6.0","svelte-check":"^4.0.0","@tsconfig/svelte":"^5.0.4"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"tmp":"tmp/bimmerz-ui_0.2.0_1781452416738_0.27630577328270767","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"_id":"@emdzej/bimmerz-ui@0.3.0","bugs":{"url":"https://github.com/emdzej/bimmerz-core/issues"},"dist":{"shasum":"4a199de2dd7fbf4f41b63ff5e09837de8be70b33","tarball":"https://registry.npmjs.org/@emdzej/bimmerz-ui/-/bimmerz-ui-0.3.0.tgz","fileCount":7,"integrity":"sha512-EGTg6zSSRGo674+OHJVwiDTFCJOVui1WXR7lJ61Bsju8f+xq3buDbKvdYQAOacL7op3piptFjxI45SUnGg86DQ==","signatures":[{"sig":"MEYCIQC6NSWBxcgMHUyJzOGNWccHaoSx8MUAp35OdA0LuVCGzQIhAPFaxGIB0VGQz9qmDL0w1lyqtbjNyWi2fABEjlabVE9Z","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDXM9qJ/+hRUYBeMYW6iedFOoUWtyIFEC0FHCuViLFvzAIgEM2yJTf4cb6+2CliDKU2ydFbsQ2bJoA16hc1qn6jBbo="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@emdzej%2fbimmerz-ui@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":21684},"name":"@emdzej/bimmerz-ui","type":"module","_from":"file:emdzej-bimmerz-ui-0.3.0.tgz","types":"./src/index.ts","svelte":"./src/index.ts","exports":{".":{"types":"./src/index.ts","import":"./src/index.ts","svelte":"./src/index.ts"}},"license":"PolyForm-Noncommercial-1.0.0","scripts":{"lint":"eslint .","test":"vitest run --passWithNoTests","build":"svelte-check --tsconfig ./tsconfig.json","clean":"rm -rf .turbo *.tsbuildinfo","typecheck":"svelte-check --tsconfig ./tsconfig.json"},"version":"0.3.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:de6f7c52-e2b0-4372-9516-333c43fae744"}},"homepage":"https://github.com/emdzej/bimmerz-core#readme","_resolved":"/tmp/361ab9f853e304703fb37c2f1119d2cb/emdzej-bimmerz-ui-0.3.0.tgz","_integrity":"sha512-EGTg6zSSRGo674+OHJVwiDTFCJOVui1WXR7lJ61Bsju8f+xq3buDbKvdYQAOacL7op3piptFjxI45SUnGg86DQ==","repository":{"url":"git+https://github.com/emdzej/bimmerz-core.git","type":"git","directory":"packages/ui"},"_npmVersion":"12.1.0","description":"Shared Svelte 5 components + lifecycle hooks for the bimmerz app family — brand wordmark, M-stripe, embedded-mode auto-connect hook. Source-only — consumer apps' Vite compiles them.","directories":{},"maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"svelte":"^5.57.1","typescript":"^5.6.0","svelte-check":"^4.7.6","@tsconfig/svelte":"^5.0.4"},"peerDependencies":{"svelte":"^5.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bimmerz-ui_0.3.0_1790327330560_0.8672251545867489"}}},"time":{"created":"2026-06-12T09:52:19.276Z","modified":"2026-09-25T09:08:50.936Z","0.1.0":"2026-06-12T09:52:19.800Z","0.2.0":"2026-06-14T15:53:36.883Z","0.3.0":"2026-09-25T09:08:50.643Z"},"bugs":{"url":"https://github.com/emdzej/bimmerz-core/issues"},"homepage":"https://github.com/emdzej/bimmerz-core#readme","repository":{"url":"git+https://github.com/emdzej/bimmerz-core.git","type":"git","directory":"packages/ui"},"description":"Shared Svelte 5 components + lifecycle hooks for the bimmerz app family — brand wordmark, M-stripe, embedded-mode auto-connect hook. Source-only — consumer apps' Vite compiles them.","maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"readme":"# @emdzej/bimmerz-ui\n\nShared **Svelte 5** components + lifecycle hooks for the bimmerz app\nfamily (dashx, ediabasx, inpax, ncsx, …). Source-only — consumer\napps' Vite + svelte-plugin compiles them. No pre-compiled output.\n\n## Convention: source-only Svelte libs\n\nSvelte components ship as `.svelte` files, rune helpers as\n`.svelte.ts` — both compiled by the consumer's Svelte plugin. The\n`package.json` carries:\n\n```json\n{\n  \"svelte\": \"./src/index.ts\",\n  \"types\": \"./src/index.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./src/index.ts\",\n      \"svelte\": \"./src/index.ts\",\n      \"import\": \"./src/index.ts\"\n    }\n  }\n}\n```\n\nThe `svelte` export condition is what the consumer's Vite plugin\nreads to locate the source. The `types` condition lets\n`svelte-check` resolve them the same way without an explicit\n`.d.ts` step.\n\n**Why:** Svelte 5's compiler output depends on the consumer's\ncompiler version + rune config. Pre-compiling locks the output to\nwhichever Svelte version the lib was built against and breaks\ntree-shaking + HMR in the consuming app. Letting consumers compile\nmeans every version, every dev mode, every runes setting works.\n\nA consumer's `vite.config.ts` should also tell `optimizeDeps`\nabout us so the dev server pre-bundles the source:\n\n```ts\noptimizeDeps: {\n  include: [\"@emdzej/bimmerz-ui\"],\n}\n```\n\n## Required peer setup\n\nComponents use semantic colour tokens (`bg-surface`,\n`text-foreground`, `text-accent`, `m-stripe`, …) from\n[`@emdzej/bimmerz-theme`](../theme). Wire that preset into the\nconsumer app's `tailwind.config.ts` and import its `tokens.css`\nfirst; the components break visually without those tokens.\n\n## Components\n\n| Name | Purpose |\n|---|---|\n| `<Brand body suffix class? />` | Split-colour wordmark — body in foreground, suffix in accent. |\n| `<MStripe class? />` | BMW M tricolour bar (light-blue / dark-blue / red) used as the page-top signature. Renders the `.m-stripe` element from `@emdzej/bimmerz-theme/tokens.css`. |\n\n### `<Brand>`\n\n```svelte\n<script lang=\"ts\">\n  import { Brand } from \"@emdzej/bimmerz-ui\";\n</script>\n\n<Brand body=\"EDIABAS\" suffix=\"X\" />\n<Brand body=\"DASH\"    suffix=\"X\" class=\"text-2xl\" />\n```\n\nDefault styling is `text-sm font-bold tracking-wide`. Pass extra\nTailwind classes via `class` for hero / welcome-screen variants.\nEach consumer app picks its own `accent` colour via\n`tailwind.config.ts` extend.\n\n### `<MStripe>`\n\n```svelte\n<script lang=\"ts\">\n  import { MStripe } from \"@emdzej/bimmerz-ui\";\n</script>\n\n<MStripe />\n<MStripe class=\"h-2\" />  <!-- thicker variant -->\n```\n\nRenders three bands (`m-stripe__band--light`, `--dark`, `--red`).\nColours resolve through the `.m-stripe` CSS in\n`@emdzej/bimmerz-theme/tokens.css` — consumer must have those\ntokens imported. Default height is 4 px.\n\n## Hooks\n\n### `useEmbeddedAutoConnect`\n\nEmbedded-mode lifecycle hook for every dongle-hosted app. Browser\nbuilds keep the manual \"Connect\" button; embedded builds (those\nserved by the bimmerz-box at `/dashx/`, `/inpax/`, …) wire this\nhook into `App.svelte` to **auto-connect on open**, **auto-disconnect\non close**, and **auto-reconnect with exponential backoff** on\ntransient transport drops.\n\nThe hook is a **no-op when `isEmbedded === false`**, so calling it\nunconditionally from `App.svelte` is the intended pattern. Must be\ncalled inside a Svelte 5 component context (uses `$effect`).\n\n```svelte\n<script lang=\"ts\">\n  import { useEmbeddedAutoConnect } from \"@emdzej/bimmerz-ui\";\n  import { isEmbedded } from \"./lib/embedded\";\n  import { app } from \"./lib/state.svelte\";\n  import { connect, disconnect } from \"./lib/connection.svelte\";\n\n  useEmbeddedAutoConnect({\n    isEmbedded,\n    connect,\n    disconnect,\n    /* Optional readiness gate — inpax/ncsx wait for the install to\n       load before connecting; dashx/ediabasx can omit this. */\n    isReady: () => app.install !== null,\n    /* Drives the auto-reconnect loop: when this flips back to\n       false (transient Wi-Fi drop, dongle reboot, bus-off), the\n       hook re-enters the backoff retry loop. */\n    isConnected: () => app.status.kind === \"connected\",\n  });\n</script>\n```\n\n**Behaviour:**\n\n| Event | Effect |\n|---|---|\n| Mount, `isEmbedded` + `isReady` true | Call `connect()` once. |\n| `connect()` throws | Retry with exponential backoff (1 → 2 → 4 → 8 → 16 → 30 s cap). Reset on success. |\n| `isConnected()` observed false later | Re-enter the backoff retry loop. |\n| `beforeunload` / `pagehide` | Fire-and-forget `disconnect()` so the dongle WebSocket closes cleanly. Both events handled — `pagehide` covers mobile Safari's bfcache. |\n| `isEmbedded` false | Hook does nothing. Manual Connect button stays in charge. |\n\n**Options:**\n\n```ts\ninterface AutoConnectOptions {\n  isEmbedded: boolean;\n  connect: () => Promise<void>;\n  disconnect: () => Promise<void>;\n  isReady?: () => boolean;          // default: always ready\n  isConnected?: () => boolean;      // omit to attempt only once\n  maxBackoffMs?: number;            // default 30_000\n  initialBackoffMs?: number;        // default 1_000\n  log?: (msg: string, level?: \"info\" | \"warn\" | \"error\") => void;\n}\n```\n\n**Where to use it:**\n\n- **dashx-web** — `connect()` opens the RPC CAN session at\n  `${origin}/rpc/can/0`. No `isReady` gate needed.\n- **ediabasx-web** — `connect()` opens the ediabasx RPC session at\n  `${origin}/rpc/ediabasx`. No `isReady` gate needed.\n- **inpax-web** / **ncsx-web** — `connect()` opens the ediabasx-server\n  RPC session over WebSocket. `isReady` should return true once the\n  remote install has loaded; otherwise the hook idles until then.\n\n**Why centralise this**: every embedded app needs the same\nlifecycle (connect on mount, clean disconnect on close, retry on\ntransient failure). Without the shared hook each app drifts —\ndifferent backoff curves, different unload event sets, different\nreconnect semantics. One implementation, four consumers.\n\n## Versioning\n\n`0.2.0` adds the `useEmbeddedAutoConnect` hook. `Brand` + `MStripe`\nAPIs are unchanged from `0.1.x`. SemVer minor — existing consumers\nupgrade without code changes.\n","readmeFilename":"README.md","license":"PolyForm-Noncommercial-1.0.0"}