@thebase/ui 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## [v1.1.1] - 2026-10-04
6
+
7
+ ### Added
8
+ - Analytics: `visitorContext()` export from `@thebase/ui/analytics` (and `window.thebase.analytics.visitorContext`). It returns the visitor's timezone (`tz`), browser language (`lang`) and a coarse screen bucket (`screen`: Mobile/Tablet/Laptop/Desktop). Pass it as `customProperties` to attach them to every event. Types are included as `BaseUIVisitorContext`. It's an additive export released as a patch by maintainer decision; nothing existing changed.
9
+ - Docs: `docs/analytics.md` covers receiving events on your own backend (payload reference, Go Fiber v3 and FastAPI receivers), a "Sample dashboard" section, and "Visitor context". README has an Analytics section.
10
+ - Examples: `examples/analytics/` contains a BaseUI analytics dashboard plus runnable Go Fiber and FastAPI sample backends (in-memory store, `GET /api/stats`, `DEMO_SEED=1` demo data). The landing page has an Analytics section, and the docs nav lists the Analytics guide.
11
+
12
+ ### Changed
13
+ - Docs and examples: production pin examples now use `@1.1.1`.
14
+ - `npm run dev` server: folder URLs serve their `index.html`, and `.go`/`.py`/`.mod` sources are served as text.
15
+
5
16
  ## [v1.1.0] - 2026-10-04
6
17
 
7
18
  ### Added
package/README.md CHANGED
@@ -4,7 +4,7 @@ The Base UI is a CDN-installable UI kit built with Bootstrap 5 styling and Owl-e
4
4
 
5
5
  Its strong points: **no build step** — every component runs straight from a CDN `<script>`/import map, nothing to compile; **modular component design** — each component is a self-contained unit you import or mark up individually, not a monolith; **vanilla JavaScript/TypeScript** — no framework lock-in, no JSX, no compiler-specific syntax to learn; and this combination makes it well suited to building **WYSIWYG website builders**, where components must mount, unmount, and re-render dynamically from user-driven markup or drag-and-drop edits rather than a fixed build-time component tree.
6
6
 
7
- Both APIs load straight from a CDN — jsDelivr or unpkg — no build step, no self-hosting required. The snippets below use the `@latest` tag for readability; pin an exact version instead (e.g. `@1.1.0`, or `@1` to take only non-breaking updates) for any production page, since `@latest` can change what your page loads the moment a new version ships. Pure Owl usage:
7
+ Both APIs load straight from a CDN — jsDelivr or unpkg — no build step, no self-hosting required. The snippets below use the `@latest` tag for readability; pin an exact version instead (e.g. `@1.1.1`, or `@1` to take only non-breaking updates) for any production page, since `@latest` can change what your page loads the moment a new version ships. Pure Owl usage:
8
8
 
9
9
  ```html
10
10
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.min.css">
@@ -56,9 +56,23 @@ For app-level theme controls, import `createThemeController`/`BaseUITheme` as `@
56
56
 
57
57
  Every component ships both APIs from `src/components/<name>/`: the static `b-ui` adapter (`<name>.js`, using `b-att-*` attributes and `baseui:*` events) and a pure Owl component class (`<name>.component.js`, exported from `@thebase/ui/components`). A few components' pure Owl treatment is intentionally lighter or reshaped versus their static markup-driven adapter — most notably `chart` (a slotted container; static Chart.js bootstrapping stays a `b-ui`-only concern) and the data-driven `data-table`/`combobox`/`command`/`calendar` (props-driven instead of slot/markup-driven). See each component's doc page under `docs/components/` for specifics.
58
58
 
59
+ ## Analytics
60
+
61
+ BaseUI also ships an optional, cookieless web-analytics service (from 1.1.0). It is a standalone bundle — not part of `dist/baseui.min.js`/`baseui.esm.js`, with no Owl, Bootstrap, or component runtime dependency (~5.6 KB minified) — so you can load it with or without the rest of BaseUI. Nothing is collected until the page calls `init()`:
62
+
63
+ ```html
64
+ <script type="module">
65
+ import { init, track } from "https://cdn.jsdelivr.net/npm/@thebase/ui@latest/dist/baseui.analytics.esm.js";
66
+ init({ backendUrl: "https://stats.example.com" }); // events POST to <backendUrl>/api/event
67
+ track("Signup", { props: { plan: "pro" } });
68
+ </script>
69
+ ```
70
+
71
+ From npm, use `import { init, track } from "@thebase/ui/analytics"`; for a classic `<script>` tag, `dist/baseui.analytics.min.js` sets `window.thebase.analytics`. It covers automatic and SPA pageviews, engagement (time on page and scroll depth), outbound links, file downloads, form submissions, and `thebase-event-*` class tagging. See [Analytics](docs/analytics.md) for every option, the payload format, opt-out, and sample Go Fiber / FastAPI backends. [`examples/analytics/`](examples/analytics/) holds a ready-to-run BaseUI dashboard for the collected data.
72
+
59
73
  ## Release notes
60
74
 
61
- See [CHANGELOG.md](CHANGELOG.md) for the full version history. Current version: **1.1.0**.
75
+ See [CHANGELOG.md](CHANGELOG.md) for the full version history. Current version: **1.1.1**.
62
76
 
63
77
  ## Versioning policy
64
78
 
@@ -77,7 +91,7 @@ What each release type means:
77
91
 
78
92
  Anything prefixed `_`, undocumented internals, and the `docs/`/`examples/` sources are not part of the public API. Neither are the source-level authoring helpers described in [Component Authoring](docs/component-authoring.md) (`optional()`, `slotProps`, the `open-state.js` hooks, `bindBootstrapOverlay()`): they aren't exported from the package and may change in any release. Deprecated APIs keep working until the next major and are flagged in the CHANGELOG first.
79
93
 
80
- Pinning: use an exact version (`@1.1.0`) when you need byte-identical output, or the major range (`@1` on jsDelivr/unpkg, `^1.0.0` in npm) to receive fixes and features without breaking changes. Avoid `@latest` in production — it will move to the next major.
94
+ Pinning: use an exact version (`@1.1.1`) when you need byte-identical output, or the major range (`@1` on jsDelivr/unpkg, `^1.0.0` in npm) to receive fixes and features without breaking changes. Avoid `@latest` in production — it will move to the next major.
81
95
 
82
96
  ## Project Documents
83
97
 
@@ -131,6 +145,6 @@ BaseUI includes the full Lucide SVG icon set as a built-in icon library. Use `<s
131
145
 
132
146
  ## Distribution
133
147
 
134
- The npm package publishes the built `dist/` artifacts plus package metadata. The browser build bundles Bootstrap CSS, and `dist/baseui.min.js`/`baseui.esm.js` embed Bootstrap's JS bundle, the Owl runtime, every pure Owl component class, and the theme helper directly (Bootstrap then Owl then BaseUI, in that load order) — consumers install BaseUI with one stylesheet and one script tag for either API. Bootstrap's JS drives every component that has a Bootstrap JS equivalent (modal, dropdown, collapse, tab, toast, carousel, tooltip, popover, alert); BaseUI's `.behavior.js`/`runtime/*.js` helpers wrap Bootstrap's JS API instead of reimplementing it, for both the static and pure-Owl APIs. `dist/bootstrap.bundle.min.js` also ships standalone (unmodified) and `dist/baseui.icons.esm.js` stays a separate, deliberate exception (2000+ generated icon components would blow the main bundle's size budget) — everything else is one file. Every shipped CSS/JS artifact is minified — there are no unminified twins to keep in sync.
148
+ The npm package publishes the built `dist/` artifacts plus package metadata. The browser build bundles Bootstrap CSS, and `dist/baseui.min.js`/`baseui.esm.js` embed Bootstrap's JS bundle, the Owl runtime, every pure Owl component class, and the theme helper directly (Bootstrap then Owl then BaseUI, in that load order) — consumers install BaseUI with one stylesheet and one script tag for either API. Bootstrap's JS drives every component that has a Bootstrap JS equivalent (modal, dropdown, collapse, tab, toast, carousel, tooltip, popover, alert); BaseUI's `.behavior.js`/`runtime/*.js` helpers wrap Bootstrap's JS API instead of reimplementing it, for both the static and pure-Owl APIs. `dist/bootstrap.bundle.min.js` also ships standalone (unmodified) and `dist/baseui.icons.esm.js` stays a separate, deliberate exception (2000+ generated icon components would blow the main bundle's size budget), as does the opt-in analytics service (`dist/baseui.analytics.esm.js`/`.min.js`, so pages that don't track pay nothing for it) — everything else is one file. Every shipped CSS/JS artifact is minified — there are no unminified twins to keep in sync.
135
149
 
136
150
  The published tarball also carries `docs/*.md` and `docs/components/` (see the "Project Documents" section above), `README.md`, `CHANGELOG.md`, and `LICENSE` — `docs/v2/` and `docs/component-authoring.md` are deliberately excluded from `package.json`'s `files` list since they're development-time-only references for working inside this repo.
@@ -2,6 +2,7 @@
2
2
  import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { spawnSync } from "node:child_process";
4
4
  import path from "node:path";
5
+ import { pathToFileURL } from "node:url";
5
6
 
6
7
  const ownPackageJson = JSON.parse(
7
8
  readFileSync(new URL("../package.json", import.meta.url), "utf8"),
@@ -16,13 +17,18 @@ function toPackageName(name) {
16
17
  .replace(/^-+|-+$/g, "") || "baseui-app";
17
18
  }
18
19
 
19
- /** Builds the fixed set of Vite + BaseUI scaffold files for a project named `appName`. */
20
- function buildTemplateFiles(appName, baseuiVersion) {
21
- const title = appName
20
+ /** Turns a kebab/snake-case app name into a human-readable title, e.g. "qr-page" -> "Qr Page". */
21
+ function toTitle(appName) {
22
+ return appName
22
23
  .split(/[-_]/)
23
24
  .filter(Boolean)
24
25
  .map((word) => word[0].toUpperCase() + word.slice(1))
25
26
  .join(" ");
27
+ }
28
+
29
+ /** Builds the fixed set of Vite + BaseUI scaffold files for a project named `appName`. */
30
+ function buildTemplateFiles(appName, baseuiVersion) {
31
+ const title = toTitle(appName);
26
32
 
27
33
  return {
28
34
  "package.json": JSON.stringify(
@@ -140,6 +146,375 @@ npm run dev
140
146
  };
141
147
  }
142
148
 
149
+ /** Builds one page's HTML shell: mounts #app and loads that page's own entry script. */
150
+ function pageHtml(title, entryScript) {
151
+ return `<!doctype html>
152
+ <html lang="en" b-theme="light">
153
+ <head>
154
+ <meta charset="utf-8" />
155
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
156
+ <title>${title}</title>
157
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
158
+ </head>
159
+ <body class="bg-body text-body">
160
+ <div id="app"></div>
161
+ <script type="module" src="${entryScript}"></script>
162
+ </body>
163
+ </html>
164
+ `;
165
+ }
166
+
167
+ /** Builds one page's entry script: loads its templates/component and mounts via the shared mountPage helper. */
168
+ function pageMain(componentName, templatesImportPath, componentImportPath) {
169
+ return `import "@thebase/ui/baseui.css";
170
+ import pageTemplates from "${templatesImportPath}?raw";
171
+ import { ${componentName} } from "${componentImportPath}";
172
+ import { mountPage } from "../mount-page.js";
173
+ import "../style.css";
174
+
175
+ await mountPage(${componentName}, pageTemplates);
176
+ `;
177
+ }
178
+
179
+ /** Builds a 4-page Vite + BaseUI starter (landing, login, signup, an authenticated home shell) for a
180
+ * project named `appName` — each page is its own Owl root mounted by its own HTML entry, composed
181
+ * from the same blocks as `examples/blocks/login-03.html`, `signup-03.html` and `sidebar-02.html`. */
182
+ function buildStarterFiles(appName, baseuiVersion) {
183
+ const title = toTitle(appName);
184
+
185
+ return {
186
+ "package.json": JSON.stringify(
187
+ {
188
+ name: appName,
189
+ private: true,
190
+ version: "0.0.1",
191
+ type: "module",
192
+ scripts: {
193
+ dev: "vite",
194
+ build: "vite build",
195
+ preview: "vite preview",
196
+ },
197
+ dependencies: {
198
+ "@thebase/ui": `^${baseuiVersion}`,
199
+ },
200
+ devDependencies: {
201
+ vite: "^8.2.1",
202
+ },
203
+ },
204
+ null,
205
+ 2,
206
+ ) + "\n",
207
+
208
+ "vite.config.js": `import { defineConfig } from "vite";
209
+ import { fileURLToPath } from "node:url";
210
+
211
+ /** Resolves a file in this config's own directory, Node-ESM-safe (no __dirname in ESM). */
212
+ const here = (file) => fileURLToPath(new URL(file, import.meta.url));
213
+
214
+ export default defineConfig({
215
+ server: { open: true },
216
+ build: {
217
+ rollupOptions: {
218
+ input: {
219
+ main: here("./index.html"),
220
+ login: here("./login.html"),
221
+ signup: here("./signup.html"),
222
+ home: here("./home.html"),
223
+ },
224
+ },
225
+ },
226
+ });
227
+ `,
228
+
229
+ ".gitignore": `node_modules/\ndist/\n`,
230
+
231
+ "public/favicon.svg": `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
232
+ fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
233
+ <rect width="24" height="24" rx="5" fill="#0f172a" stroke="none" />
234
+ <path d="M8 12h8" />
235
+ <path d="M12 8v8" />
236
+ </svg>
237
+ `,
238
+
239
+ "src/style.css": `/* App-specific styles layered on top of @thebase/ui/baseui.css go here. */\n`,
240
+
241
+ "src/mount-page.js": `import { mount } from "@thebase/ui";
242
+ import baseuiTemplates from "@thebase/ui/dist/baseui.templates.xml?raw";
243
+
244
+ /** Both files are full <templates>...</templates> documents; strip each one's outer wrapper before
245
+ * joining them, otherwise the merged string has two root elements and Owl's XML parser throws. */
246
+ const stripTemplatesWrapper = (xml) =>
247
+ xml.replace(/^\\s*<templates[^>]*>/, "").replace(/<\\/templates>\\s*$/, "");
248
+
249
+ /** Merges BaseUI's component templates with one page's own templates and mounts its root component. */
250
+ export async function mountPage(Root, pageTemplatesXml) {
251
+ const templates = \`<templates>\${stripTemplatesWrapper(baseuiTemplates)}\${stripTemplatesWrapper(pageTemplatesXml)}</templates>\`;
252
+ await mount(Root, document.getElementById("app"), { templates });
253
+ }
254
+ `,
255
+
256
+ "index.html": pageHtml(title, "/src/landing/main.js"),
257
+ "src/landing/main.js": pageMain("Landing", "./Landing.xml", "./Landing.js"),
258
+ "src/landing/Landing.js": `import { Component } from "@thebase/ui";
259
+ import { Button, Card, CardBody, CardHeader, CardTitle, Icon } from "@thebase/ui";
260
+
261
+ /** Marketing landing page: header with a login link, hero pitch, feature grid, footer. */
262
+ export class Landing extends Component {
263
+ static template = "app.Landing";
264
+ static components = { Button, Card, CardBody, CardHeader, CardTitle, Icon };
265
+ }
266
+ `,
267
+ "src/landing/Landing.xml": `<templates xml:space="preserve">
268
+ <t t-name="app.Landing">
269
+ <div class="d-flex flex-column min-vh-100">
270
+ <header class="border-bottom">
271
+ <div class="container d-flex align-items-center justify-content-between py-3">
272
+ <a href="/" class="d-inline-flex align-items-center gap-2 fw-medium text-decoration-none">
273
+ <Icon name="'gallery-vertical-end'"/>
274
+ ${title}
275
+ </a>
276
+ <Button variant="'outline'" onClick="() => window.location.assign('/login.html')">Login</Button>
277
+ </div>
278
+ </header>
279
+
280
+ <section class="container py-5 text-center">
281
+ <h1 class="display-5 fw-bold mb-3">Build faster with ${title}</h1>
282
+ <p class="lead text-muted mx-auto mb-4" style="max-width: 40rem">
283
+ A starter scaffold wired up with BaseUI — pure Owl components, Bootstrap styling, and the pages you need to ship day one.
284
+ </p>
285
+ <div class="d-flex justify-content-center gap-3">
286
+ <Button icon="'rocket'" onClick="() => window.location.assign('/signup.html')">Get started</Button>
287
+ <Button variant="'outline'" onClick="() => window.location.assign('/login.html')">Login</Button>
288
+ </div>
289
+ </section>
290
+
291
+ <section class="container py-5">
292
+ <div class="row g-4">
293
+ <div class="col-12 col-md-4">
294
+ <Card className="'h-100'">
295
+ <CardHeader className="'d-flex align-items-center gap-2'"><Icon name="'zap'"/><CardTitle>Fast by default</CardTitle></CardHeader>
296
+ <CardBody>Vite dev server and production build, pre-wired to BaseUI's CDN or local dist.</CardBody>
297
+ </Card>
298
+ </div>
299
+ <div class="col-12 col-md-4">
300
+ <Card className="'h-100'">
301
+ <CardHeader className="'d-flex align-items-center gap-2'"><Icon name="'layout-dashboard'"/><CardTitle>Pages included</CardTitle></CardHeader>
302
+ <CardBody>Landing, login, signup and an authenticated home shell, ready to extend.</CardBody>
303
+ </Card>
304
+ </div>
305
+ <div class="col-12 col-md-4">
306
+ <Card className="'h-100'">
307
+ <CardHeader className="'d-flex align-items-center gap-2'"><Icon name="'component'"/><CardTitle>Pure Owl components</CardTitle></CardHeader>
308
+ <CardBody>Every page composes real BaseUI components — swap or extend without fighting the markup.</CardBody>
309
+ </Card>
310
+ </div>
311
+ </div>
312
+ </section>
313
+
314
+ <footer class="border-top mt-auto">
315
+ <div class="container py-4 d-flex align-items-center justify-content-between">
316
+ <small class="text-muted">© ${title}. All rights reserved.</small>
317
+ <a href="/login.html" class="small text-decoration-underline">Login</a>
318
+ </div>
319
+ </footer>
320
+ </div>
321
+ </t>
322
+ </templates>
323
+ `,
324
+
325
+ "login.html": pageHtml(`Login — ${title}`, "/src/login/main.js"),
326
+ "src/login/main.js": pageMain("Login", "./Login.xml", "./Login.js"),
327
+ "src/login/Login.js": `import { Component } from "@thebase/ui";
328
+ import { Button, Card, CardBody, Field, FieldGroup, Icon, Input, Label } from "@thebase/ui";
329
+
330
+ /** Login page (ported from examples/blocks/login-03.html): email/password card, redirects home on submit. */
331
+ export class Login extends Component {
332
+ static template = "app.Login";
333
+ static components = { Button, Card, CardBody, Field, FieldGroup, Icon, Input, Label };
334
+ }
335
+ `,
336
+ "src/login/Login.xml": `<templates xml:space="preserve">
337
+ <t t-name="app.Login">
338
+ <div class="d-flex flex-column align-items-center justify-content-center gap-4 min-vh-100 bg-body-tertiary p-4">
339
+ <div class="d-flex flex-column align-items-center gap-4 col-12 col-sm-8 col-md-6 col-lg-4">
340
+ <a href="/" class="d-inline-flex align-items-center gap-2 fw-medium text-decoration-none">
341
+ <Icon name="'gallery-vertical-end'"/>
342
+ ${title}
343
+ </a>
344
+ <Card className="'w-100'">
345
+ <CardBody>
346
+ <form class="d-flex flex-column gap-4">
347
+ <div class="d-flex flex-column gap-2 text-center">
348
+ <h2 class="h4 mb-0">Login to your account</h2>
349
+ <p class="text-muted mb-0">Enter your email below to login to your account</p>
350
+ </div>
351
+ <FieldGroup>
352
+ <Field>
353
+ <Label>Email</Label>
354
+ <Input type="'email'" placeholder="'m@example.com'"/>
355
+ </Field>
356
+ <Field>
357
+ <div class="d-flex align-items-center justify-content-between">
358
+ <Label>Password</Label>
359
+ <a href="#" class="small text-decoration-underline">Forgot your password?</a>
360
+ </div>
361
+ <Input type="'password'"/>
362
+ </Field>
363
+ <Button icon="'log-in'" onClick="() => window.location.assign('/home.html')">Login</Button>
364
+ <Button variant="'outline'" icon="'mail'">Login with email</Button>
365
+ </FieldGroup>
366
+ <p class="text-center small mb-0">Don&apos;t have an account? <a href="/signup.html" class="text-decoration-underline">Sign up</a></p>
367
+ </form>
368
+ </CardBody>
369
+ </Card>
370
+ </div>
371
+ </div>
372
+ </t>
373
+ </templates>
374
+ `,
375
+
376
+ "signup.html": pageHtml(`Sign up — ${title}`, "/src/signup/main.js"),
377
+ "src/signup/main.js": pageMain("Signup", "./Signup.xml", "./Signup.js"),
378
+ "src/signup/Signup.js": `import { Component } from "@thebase/ui";
379
+ import { Button, Card, CardBody, Field, FieldGroup, Icon, Input, Label } from "@thebase/ui";
380
+
381
+ /** Signup page (ported from examples/blocks/signup-03.html): single email field, sends the user to login after submit. */
382
+ export class Signup extends Component {
383
+ static template = "app.Signup";
384
+ static components = { Button, Card, CardBody, Field, FieldGroup, Icon, Input, Label };
385
+ }
386
+ `,
387
+ "src/signup/Signup.xml": `<templates xml:space="preserve">
388
+ <t t-name="app.Signup">
389
+ <div class="d-flex flex-column align-items-center justify-content-center gap-4 min-vh-100 bg-body-tertiary p-4">
390
+ <div class="d-flex flex-column align-items-center gap-4 col-12 col-sm-8 col-md-6 col-lg-4">
391
+ <a href="/" class="d-inline-flex align-items-center gap-2 fw-medium text-decoration-none">
392
+ <Icon name="'gallery-vertical-end'"/>
393
+ ${title}
394
+ </a>
395
+ <Card className="'w-100'">
396
+ <CardBody>
397
+ <form class="d-flex flex-column gap-4">
398
+ <div class="d-flex flex-column gap-2 text-center">
399
+ <h2 class="h4 mb-0">Create an account</h2>
400
+ <p class="text-muted mb-0">Enter your email below to create your account</p>
401
+ </div>
402
+ <FieldGroup>
403
+ <Field>
404
+ <Label>Email</Label>
405
+ <Input type="'email'" placeholder="'m@example.com'"/>
406
+ </Field>
407
+ <Button icon="'user-plus'" onClick="() => window.location.assign('/login.html')">Sign up</Button>
408
+ <Button variant="'outline'" icon="'mail'">Sign up with email</Button>
409
+ </FieldGroup>
410
+ <p class="text-center small mb-0">Already have an account? <a href="/login.html" class="text-decoration-underline">Login</a></p>
411
+ </form>
412
+ </CardBody>
413
+ </Card>
414
+ </div>
415
+ </div>
416
+ </t>
417
+ </templates>
418
+ `,
419
+
420
+ "home.html": pageHtml(`Home — ${title}`, "/src/home/main.js"),
421
+ "src/home/main.js": pageMain("Home", "./Home.xml", "./Home.js"),
422
+ "src/home/Home.js": `import { Component, useState } from "@thebase/ui";
423
+ import {
424
+ Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator,
425
+ Button, Icon, InputGroup, InputGroupAddon, InputGroupInput, Separator,
426
+ Shell, ShellContent, ShellHeader, ShellMain, ShellNav, ShellSidebar, ShellSidebarHeader,
427
+ SidebarGroupCollapsible, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarTrigger,
428
+ } from "@thebase/ui";
429
+
430
+ /** Authenticated home shell (ported from examples/blocks/sidebar-02.html): collapsible sidebar nav
431
+ * + sticky header, reached after login/signup; owns the sidebar's collapsed/expanded state. */
432
+ export class Home extends Component {
433
+ static template = "app.Home";
434
+ static components = {
435
+ Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator,
436
+ Button, Icon, InputGroup, InputGroupAddon, InputGroupInput, Separator,
437
+ Shell, ShellContent, ShellHeader, ShellMain, ShellNav, ShellSidebar, ShellSidebarHeader,
438
+ SidebarGroupCollapsible, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarTrigger,
439
+ };
440
+
441
+ setup() {
442
+ this.state = useState({ collapsed: false });
443
+ }
444
+ }
445
+ `,
446
+ "src/home/Home.xml": `<templates xml:space="preserve">
447
+ <t t-name="app.Home">
448
+ <Shell className="'border'" collapsed="state.collapsed" onCollapsedChange="(value) => { state.collapsed = value; }">
449
+ <ShellSidebar>
450
+ <ShellSidebarHeader>
451
+ <a href="/" class="d-flex align-items-center gap-2 text-decoration-none fw-medium">
452
+ <Icon name="'gallery-vertical-end'"/>
453
+ <span class="d-flex flex-column lh-sm">
454
+ <span>${title}</span>
455
+ <small class="text-body-secondary fw-normal">Dashboard</small>
456
+ </span>
457
+ </a>
458
+ </ShellSidebarHeader>
459
+ <InputGroup>
460
+ <InputGroupAddon><Icon name="'search'"/></InputGroupAddon>
461
+ <InputGroupInput type="'search'" placeholder="'Search...'"/>
462
+ </InputGroup>
463
+ <ShellNav className="'d-flex flex-column gap-2'">
464
+ <SidebarGroupCollapsible label="'Getting Started'" defaultOpen="true">
465
+ <SidebarMenu>
466
+ <SidebarMenuItem><SidebarMenuButton href="'#'" icon="'layout-dashboard'" active="true">Overview</SidebarMenuButton></SidebarMenuItem>
467
+ <SidebarMenuItem><SidebarMenuButton href="'#'" icon="'users'">Customers</SidebarMenuButton></SidebarMenuItem>
468
+ <SidebarMenuItem><SidebarMenuButton href="'#'" icon="'settings'">Settings</SidebarMenuButton></SidebarMenuItem>
469
+ </SidebarMenu>
470
+ </SidebarGroupCollapsible>
471
+ </ShellNav>
472
+ </ShellSidebar>
473
+ <ShellMain>
474
+ <ShellHeader className="'border-bottom p-3'">
475
+ <SidebarTrigger/>
476
+ <Separator orientation="'vertical'"/>
477
+ <Breadcrumb>
478
+ <BreadcrumbList>
479
+ <BreadcrumbItem><BreadcrumbPage>Overview</BreadcrumbPage></BreadcrumbItem>
480
+ </BreadcrumbList>
481
+ </Breadcrumb>
482
+ <Button variant="'outline'" size="'sm'" className="'ms-auto'" icon="'log-out'" onClick="() => window.location.assign('/index.html')">Logout</Button>
483
+ </ShellHeader>
484
+ <ShellContent className="'d-flex flex-column gap-3'">
485
+ <div class="row row-cols-3 g-3">
486
+ <div class="col"><div class="ratio ratio-16x9 bg-body-secondary rounded-4"></div></div>
487
+ <div class="col"><div class="ratio ratio-16x9 bg-body-secondary rounded-4"></div></div>
488
+ <div class="col"><div class="ratio ratio-16x9 bg-body-secondary rounded-4"></div></div>
489
+ </div>
490
+ <div class="flex-grow-1 bg-body-secondary rounded-4"></div>
491
+ </ShellContent>
492
+ </ShellMain>
493
+ </Shell>
494
+ </t>
495
+ </templates>
496
+ `,
497
+
498
+ "README.md": `# ${title}
499
+
500
+ Scaffolded with \`@thebase/ui\`'s CLI (\`create-baseui-app init\`): Vite + BaseUI, pure Owl API, 4 pages.
501
+
502
+ - \`/\` — landing page (header, hero, features, footer)
503
+ - \`/login.html\` — login (redirects to \`/home.html\` on submit)
504
+ - \`/signup.html\` — signup (redirects to \`/login.html\` on submit)
505
+ - \`/home.html\` — authenticated home shell (sidebar + header, logout returns to \`/\`)
506
+
507
+ None of the pages call a real backend yet — wire up \`Login\`/\`Signup\`'s button \`onClick\` handlers to
508
+ your own auth API once you have one.
509
+
510
+ \`\`\`sh
511
+ npm install
512
+ npm run dev
513
+ \`\`\`
514
+ `,
515
+ };
516
+ }
517
+
143
518
  /** Writes each template file under targetDir, skipping any that already exist (convert mode is additive/non-destructive). */
144
519
  function writeFiles(targetDir, files, { overwrite }) {
145
520
  const written = [];
@@ -173,6 +548,7 @@ function parseFlags(argv) {
173
548
  if (arg === "--no-install") flags.install = false;
174
549
  else if (arg === "--force") flags.force = true;
175
550
  else if (arg === "--version") flags.version = argv[++i];
551
+ else if (arg === "--config") flags.config = argv[++i];
176
552
  else positional.push(arg);
177
553
  }
178
554
  return { flags, positional };
@@ -205,6 +581,37 @@ function cmdCreate(argv) {
205
581
  console.log(`\nNext steps:\n cd ${rawName}${flags.install ? "" : "\n npm install"}\n npm run dev`);
206
582
  }
207
583
 
584
+ /** Scaffolds the 4-page starter (landing/login/signup/home) in a new ./<app-name> folder. */
585
+ function cmdInit(argv) {
586
+ const { flags, positional } = parseFlags(argv);
587
+ const rawName = positional[0];
588
+ if (!rawName) {
589
+ console.error("Usage: create-baseui-app init <app-name> [--version <x.y.z>] [--no-install] [--force]");
590
+ process.exit(1);
591
+ }
592
+ const appName = toPackageName(rawName);
593
+ const targetDir = path.resolve(process.cwd(), rawName);
594
+
595
+ if (existsSync(targetDir) && readdirSync(targetDir).length > 0 && !flags.force) {
596
+ console.error(`Directory "${rawName}" already exists and is not empty. Use --force to write into it anyway.`);
597
+ process.exit(1);
598
+ }
599
+
600
+ const baseuiVersion = flags.version || ownPackageJson.version;
601
+ const files = buildStarterFiles(appName, baseuiVersion);
602
+ const { written } = writeFiles(targetDir, files, { overwrite: flags.force ?? false });
603
+
604
+ console.log(`Scaffolded a 4-page Vite + BaseUI starter in ${targetDir}`);
605
+ written.forEach((f) => console.log(` create ${f}`));
606
+
607
+ if (flags.install) runInstall(targetDir);
608
+
609
+ console.log(
610
+ `\nNext steps:\n cd ${rawName}${flags.install ? "" : "\n npm install"}\n npm run dev` +
611
+ `\n\nPages: / (landing), /login.html, /signup.html, /home.html`,
612
+ );
613
+ }
614
+
208
615
  function cmdConvert(argv) {
209
616
  const { flags, positional } = parseFlags(argv);
210
617
  const targetDir = path.resolve(process.cwd(), positional[0] || ".");
@@ -248,6 +655,34 @@ function cmdConvert(argv) {
248
655
  console.log(`\nNext steps:\n npm run dev`);
249
656
  }
250
657
 
658
+ /** Generates manifest.webmanifest + sw.js for a PWA from a `baseui.pwa.config.js` the user wrote,
659
+ * using @thebase/ui's buildManifest/buildServiceWorker (see docs/usage-guide.md "PWA toolkit"). */
660
+ async function cmdPwa(argv) {
661
+ const { flags, positional } = parseFlags(argv);
662
+ const targetDir = path.resolve(process.cwd(), positional[0] || ".");
663
+ const configPath = path.resolve(targetDir, flags.config || "baseui.pwa.config.js");
664
+
665
+ if (!existsSync(configPath)) {
666
+ console.error(
667
+ `Config file not found: ${configPath}\n` +
668
+ "Create it first — export default { manifest: {...}, serviceWorker: {...} }, " +
669
+ "see docs/usage-guide.md \"PWA toolkit\" for the full config shape.",
670
+ );
671
+ process.exit(1);
672
+ }
673
+
674
+ const { manifest, serviceWorker } = (await import(pathToFileURL(configPath).href)).default;
675
+ const { buildManifest, buildServiceWorker } = await import("../dist/baseui.pwa.esm.js");
676
+
677
+ writeFileSync(
678
+ path.join(targetDir, "manifest.webmanifest"),
679
+ JSON.stringify(buildManifest(manifest), null, 2) + "\n",
680
+ );
681
+ writeFileSync(path.join(targetDir, "sw.js"), buildServiceWorker(serviceWorker));
682
+
683
+ console.log(`Wrote manifest.webmanifest and sw.js in ${targetDir}`);
684
+ }
685
+
251
686
  function printHelp() {
252
687
  console.log(`@thebase/ui project CLI
253
688
 
@@ -258,6 +693,16 @@ Usage:
258
693
  create-baseui-app convert [path] [--version <x.y.z>] [--no-install]
259
694
  Turn an existing (non-npm) folder into a Vite + BaseUI project, in place.
260
695
  Never overwrites files that already exist; only fills in what's missing.
696
+
697
+ create-baseui-app init <app-name> [--version <x.y.z>] [--no-install] [--force]
698
+ Scaffold a ready-to-start 4-page project in ./<app-name>: a landing page
699
+ (header/hero/features/footer), login, signup, and an authenticated home
700
+ shell (sidebar + header) — ported from the login-03/signup-03/sidebar-02
701
+ example blocks.
702
+
703
+ create-baseui-app pwa [path] [--config <file>]
704
+ Generate manifest.webmanifest + sw.js in [path] (default ".") from a
705
+ baseui.pwa.config.js config file (default name, override with --config).
261
706
  `);
262
707
  }
263
708
 
@@ -265,4 +710,6 @@ const [, , command, ...rest] = process.argv;
265
710
 
266
711
  if (command === "create") cmdCreate(rest);
267
712
  else if (command === "convert") cmdConvert(rest);
713
+ else if (command === "init") cmdInit(rest);
714
+ else if (command === "pwa") await cmdPwa(rest);
268
715
  else printHelp();
@@ -1 +1 @@
1
- var H=["pdf","xlsx","docx","txt","rtf","csv","exe","key","pps","ppt","pptx","7z","pkg","rar","gz","zip","avi","mov","mp4","mpeg","wmv","midi","mp3","wav","wma","dmg"];var U=/^localhost$|^127(\.[0-9]+){0,2}\.[0-9]+$|^\[::1?\]$/,F=/thebase-event-name(=|--)(.+)/,B="/api/event",$=/^(https?:\/\/|\/)/,o={},P=H,p,A,w=!1,v,C,O={},E=-1,r=0,l=0,u,s;function z(e){if(o.isInitialized)throw Error("thebase.analytics.init() can only be called once");let t=e.apiPath||B,i=e.endpoint||e.backendUrl&&`${e.backendUrl.replace(/\/+$/,"")}/${t.replace(/^\/+/,"")}`;if(!i)throw Error("thebase.analytics.init(): backendUrl (or endpoint) is required");if(!$.test(i))throw Error(`thebase.analytics.init(): "${i}" must be an http(s):// URL or a /path on this site`);Object.assign(o,e,{domain:e.domain||location.hostname,endpoint:i,autoCapturePageviews:e.autoCapturePageviews!==!1,logging:e.logging!==!1,bindToWindow:e.bindToWindow!==!1,isInitialized:!0})}function I(e,t,i){fetch(e,{method:"POST",headers:{"Content-Type":"text/plain"},keepalive:!0,body:JSON.stringify(t)}).then(n=>i?.callback?.({status:n.status})).catch(n=>i?.callback?.({error:n}))}function V(){w&&(N(),u=S(),s=L())}function q(e){v=!1,C=e.u,O=e.p,E=-1,l=0,r=Date.now(),G()}function N(){let e=M();if(v||E>=s&&e<3e3)return;E=s;let t={n:"engagement",sd:Math.round(s/u*100),d:o.domain,u:C,p:O,e,v:36};r=0,l=0,o.hashBasedRouting&&(t.h=1),I(o.endpoint,t)}function m(){document.visibilityState==="visible"&&document.hasFocus()&&r===0?r=Date.now():(document.visibilityState==="hidden"||!document.hasFocus())&&(l=M(),r=0,N())}function G(){w||(document.addEventListener("visibilitychange",m),window.addEventListener("blur",m),window.addEventListener("focus",m),w=!0)}function M(){return r?l+(Date.now()-r):l}function S(){let e=document.body,t=document.documentElement;return Math.max(e.scrollHeight,e.offsetHeight,e.clientHeight,t.scrollHeight,t.offsetHeight,t.clientHeight)}function L(){return u<=window.innerHeight?u:window.scrollY+window.innerHeight}function K(){u=S()}function W(){s=Math.max(s,L())}function j(){u=S(),s=L(),new window.ResizeObserver(K).observe(document.documentElement),document.addEventListener("scroll",W)}function g(e,t){if(!o.isInitialized)throw Error("thebase.analytics.track() can only be called after thebase.analytics.init()");let i=e==="pageview";if(i&&V(),!o.captureOnLocalhost){if(U.test(location.hostname)||location.protocol==="file:")return d(e,t,"localhost");if((window._phantom||window.__nightmare||navigator.webdriver||window.Cypress)&&!window.__thebase)return d(e,t)}try{if(localStorage.thebase_ignore==="true")return d(e,t,"localStorage flag")}catch{}let n={n:e,v:36,u:t?.u||t?.url||location.href,d:o.domain,r:document.referrer||null};if(t?.props&&(n.p=t.props),t?.interactive===!1&&(n.i=!1),t?.revenue&&(n.$=t.revenue),o.customProperties){let a=typeof o.customProperties=="function"?o.customProperties(e):o.customProperties;typeof a=="object"&&(n.p={...a,...n.p})}if(o.hashBasedRouting&&(n.h=1),typeof o.transformRequest=="function"&&(n=o.transformRequest(n),!n))return d(e,t,"transformRequest");i&&q(n),I(o.endpoint,n,t)}function d(e,t,i){i&&o.logging&&console.warn(`Ignoring Event: ${i}`),t?.callback?.(),e==="pageview"&&(v=!0)}function x(e){return e?.tagName?.toLowerCase()==="a"}function Y(e){for(;e&&(e.tagName===void 0||!x(e)||!e.href);)e=e.parentNode;return e}function J(e){return e&&typeof e.href=="string"&&e.host&&e.host!==location.host}function Q(e){return!!e&&P.includes(e.split(".").pop())}function T(e){return!!e?.classList&&[...e.classList].some(t=>F.test(t))}function y(e,t){return!!e&&t<=3&&(T(e)||y(e.parentNode,t+1))}function D(e){let t=T(e)?e:e?.parentNode,i={name:null,props:{},revenue:{}};if(!t?.classList)return i;for(let n of t.classList){let a=n.match(/thebase-event-(.+)(=|--)(.+)/);if(a){let _=a[3].replace(/\+/g," ");a[1].toLowerCase()==="name"?i.name=_:i.props[a[1]]=_}let c=n.match(/thebase-revenue-(.+)(=|--)(.+)/);c&&(i.revenue[c[1]]=c[3])}return i}function f(e){g(e.name,{props:e.props,revenue:e.revenue})}function k(e){if(e.type==="auxclick"&&e.button!==1)return;let t=Y(e.target);if(y(t,0))return;if(o.outboundLinks&&J(t))return f({name:"Outbound Link: Click",props:{url:t.href}});let i=t&&typeof t.href=="string"&&t.href.split("?")[0];o.fileDownloads&&Q(i)&&f({name:"File Download",props:{url:i}})}function R(e){if(e.type==="auxclick"&&e.button!==1)return;let t,i,n=e.target;for(let c=0;c<=3&&n;c++){if(n.tagName?.toLowerCase()==="form")return;x(n)&&(t=n),T(n)&&(i=n),n=n.parentNode}if(!i)return;let a=D(i);t&&(a.props.url=t.href),f(a)}function X(e){let t=e.target;(t.hasAttribute("novalidate")||t.checkValidity())&&!y(t,0)&&g("Form: Submission")}function Z(e){let t=D(e.target);t.name&&f(t)}function ee(){document.addEventListener("click",k),document.addEventListener("auxclick",k),Array.isArray(o.fileDownloads?.fileExtensions)&&(P=o.fileDownloads.fileExtensions),o.formSubmissions&&document.addEventListener("submit",X,!0),document.addEventListener("submit",Z),document.addEventListener("click",R),document.addEventListener("auxclick",R)}function h(e){!o.hashBasedRouting&&e&&p===location.pathname||(p=location.pathname,g("pageview"))}function b(){h(!0)}function te(...e){A.apply(this,e),b()}function ne(){!p&&document.visibilityState==="visible"&&h()}function ie(e){e.persisted&&h()}function oe(){o.hashBasedRouting?window.addEventListener("hashchange",b):(A=window.history.pushState,window.history.pushState=te,window.addEventListener("popstate",b)),document.visibilityState==="hidden"||document.visibilityState==="prerender"?document.addEventListener("visibilitychange",ne):h(),window.addEventListener("pageshow",ie)}function ae(e){z(e||{}),j(),o.autoCapturePageviews&&oe(),ee(),o.bindToWindow&&(window.thebase??(window.thebase={}),window.thebase.track=g,window.thebase.track.s="npm",window.thebase.track.v=36,window.thebase.track.l=!0)}export{H as DEFAULT_FILE_TYPES,ae as init,g as track};
1
+ var H=["pdf","xlsx","docx","txt","rtf","csv","exe","key","pps","ppt","pptx","7z","pkg","rar","gz","zip","avi","mov","mp4","mpeg","wmv","midi","mp3","wav","wma","dmg"];var U=/^localhost$|^127(\.[0-9]+){0,2}\.[0-9]+$|^\[::1?\]$/,F=/thebase-event-name(=|--)(.+)/,B="/api/event",z=/^(https?:\/\/|\/)/,o={},P=H,m,C,w=!1,v,O,A={},E=-1,r=0,l=0,u,s;function $(e){if(o.isInitialized)throw Error("thebase.analytics.init() can only be called once");let t=e.apiPath||B,i=e.endpoint||e.backendUrl&&`${e.backendUrl.replace(/\/+$/,"")}/${t.replace(/^\/+/,"")}`;if(!i)throw Error("thebase.analytics.init(): backendUrl (or endpoint) is required");if(!z.test(i))throw Error(`thebase.analytics.init(): "${i}" must be an http(s):// URL or a /path on this site`);Object.assign(o,e,{domain:e.domain||location.hostname,endpoint:i,autoCapturePageviews:e.autoCapturePageviews!==!1,logging:e.logging!==!1,bindToWindow:e.bindToWindow!==!1,isInitialized:!0})}function I(e,t,i){fetch(e,{method:"POST",headers:{"Content-Type":"text/plain"},keepalive:!0,body:JSON.stringify(t)}).then(n=>i?.callback?.({status:n.status})).catch(n=>i?.callback?.({error:n}))}function V(){w&&(M(),u=S(),s=L())}function q(e){v=!1,O=e.u,A=e.p,E=-1,l=0,r=Date.now(),G()}function M(){let e=N();if(v||E>=s&&e<3e3)return;E=s;let t={n:"engagement",sd:Math.round(s/u*100),d:o.domain,u:O,p:A,e,v:36};r=0,l=0,o.hashBasedRouting&&(t.h=1),I(o.endpoint,t)}function p(){document.visibilityState==="visible"&&document.hasFocus()&&r===0?r=Date.now():(document.visibilityState==="hidden"||!document.hasFocus())&&(l=N(),r=0,M())}function G(){w||(document.addEventListener("visibilitychange",p),window.addEventListener("blur",p),window.addEventListener("focus",p),w=!0)}function N(){return r?l+(Date.now()-r):l}function S(){let e=document.body,t=document.documentElement;return Math.max(e.scrollHeight,e.offsetHeight,e.clientHeight,t.scrollHeight,t.offsetHeight,t.clientHeight)}function L(){return u<=window.innerHeight?u:window.scrollY+window.innerHeight}function K(){u=S()}function W(){s=Math.max(s,L())}function j(){u=S(),s=L(),new window.ResizeObserver(K).observe(document.documentElement),document.addEventListener("scroll",W)}function ae(){let e=window.innerWidth;return{tz:Intl.DateTimeFormat().resolvedOptions().timeZone,lang:navigator.language,screen:e<576?"Mobile":e<992?"Tablet":e<1440?"Laptop":"Desktop"}}function g(e,t){if(!o.isInitialized)throw Error("thebase.analytics.track() can only be called after thebase.analytics.init()");let i=e==="pageview";if(i&&V(),!o.captureOnLocalhost){if(U.test(location.hostname)||location.protocol==="file:")return d(e,t,"localhost");if((window._phantom||window.__nightmare||navigator.webdriver||window.Cypress)&&!window.__thebase)return d(e,t)}try{if(localStorage.thebase_ignore==="true")return d(e,t,"localStorage flag")}catch{}let n={n:e,v:36,u:t?.u||t?.url||location.href,d:o.domain,r:document.referrer||null};if(t?.props&&(n.p=t.props),t?.interactive===!1&&(n.i=!1),t?.revenue&&(n.$=t.revenue),o.customProperties){let a=typeof o.customProperties=="function"?o.customProperties(e):o.customProperties;typeof a=="object"&&(n.p={...a,...n.p})}if(o.hashBasedRouting&&(n.h=1),typeof o.transformRequest=="function"&&(n=o.transformRequest(n),!n))return d(e,t,"transformRequest");i&&q(n),I(o.endpoint,n,t)}function d(e,t,i){i&&o.logging&&console.warn(`Ignoring Event: ${i}`),t?.callback?.(),e==="pageview"&&(v=!0)}function x(e){return e?.tagName?.toLowerCase()==="a"}function Y(e){for(;e&&(e.tagName===void 0||!x(e)||!e.href);)e=e.parentNode;return e}function J(e){return e&&typeof e.href=="string"&&e.host&&e.host!==location.host}function Z(e){return!!e&&P.includes(e.split(".").pop())}function T(e){return!!e?.classList&&[...e.classList].some(t=>F.test(t))}function y(e,t){return!!e&&t<=3&&(T(e)||y(e.parentNode,t+1))}function D(e){let t=T(e)?e:e?.parentNode,i={name:null,props:{},revenue:{}};if(!t?.classList)return i;for(let n of t.classList){let a=n.match(/thebase-event-(.+)(=|--)(.+)/);if(a){let k=a[3].replace(/\+/g," ");a[1].toLowerCase()==="name"?i.name=k:i.props[a[1]]=k}let c=n.match(/thebase-revenue-(.+)(=|--)(.+)/);c&&(i.revenue[c[1]]=c[3])}return i}function f(e){g(e.name,{props:e.props,revenue:e.revenue})}function _(e){if(e.type==="auxclick"&&e.button!==1)return;let t=Y(e.target);if(y(t,0))return;if(o.outboundLinks&&J(t))return f({name:"Outbound Link: Click",props:{url:t.href}});let i=t&&typeof t.href=="string"&&t.href.split("?")[0];o.fileDownloads&&Z(i)&&f({name:"File Download",props:{url:i}})}function R(e){if(e.type==="auxclick"&&e.button!==1)return;let t,i,n=e.target;for(let c=0;c<=3&&n;c++){if(n.tagName?.toLowerCase()==="form")return;x(n)&&(t=n),T(n)&&(i=n),n=n.parentNode}if(!i)return;let a=D(i);t&&(a.props.url=t.href),f(a)}function Q(e){let t=e.target;(t.hasAttribute("novalidate")||t.checkValidity())&&!y(t,0)&&g("Form: Submission")}function X(e){let t=D(e.target);t.name&&f(t)}function ee(){document.addEventListener("click",_),document.addEventListener("auxclick",_),Array.isArray(o.fileDownloads?.fileExtensions)&&(P=o.fileDownloads.fileExtensions),o.formSubmissions&&document.addEventListener("submit",Q,!0),document.addEventListener("submit",X),document.addEventListener("click",R),document.addEventListener("auxclick",R)}function h(e){!o.hashBasedRouting&&e&&m===location.pathname||(m=location.pathname,g("pageview"))}function b(){h(!0)}function te(...e){C.apply(this,e),b()}function ne(){!m&&document.visibilityState==="visible"&&h()}function ie(e){e.persisted&&h()}function oe(){o.hashBasedRouting?window.addEventListener("hashchange",b):(C=window.history.pushState,window.history.pushState=te,window.addEventListener("popstate",b)),document.visibilityState==="hidden"||document.visibilityState==="prerender"?document.addEventListener("visibilitychange",ne):h(),window.addEventListener("pageshow",ie)}function re(e){$(e||{}),j(),o.autoCapturePageviews&&oe(),ee(),o.bindToWindow&&(window.thebase??(window.thebase={}),window.thebase.track=g,window.thebase.track.s="npm",window.thebase.track.v=36,window.thebase.track.l=!0)}export{H as DEFAULT_FILE_TYPES,re as init,g as track,ae as visitorContext};