@arkstack/inertia 0.15.5 → 0.16.0

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/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Response } from "@arkstack/http";
2
- import { ViewFactory } from "@arkstack/view";
3
2
 
4
3
  //#region src/types.d.ts
5
4
  /**
@@ -11,8 +10,8 @@ import { ViewFactory } from "@arkstack/view";
11
10
  type PageProps = Record<string, unknown>;
12
11
  /**
13
12
  * The page object Inertia exchanges with the client. It is serialized to JSON for
14
- * Inertia XHR visits and embedded in the root template's `data-page` attribute on
15
- * the initial full-page visit.
13
+ * Inertia XHR visits and embedded in the root template's `data-page` script
14
+ * element on the initial full-page visit.
16
15
  *
17
16
  * @see https://inertiajs.com/the-protocol
18
17
  */
@@ -38,13 +37,13 @@ interface InertiaPage {
38
37
  interface InertiaConfig {
39
38
  /**
40
39
  * The root Edge template that wraps the SPA and renders the `data-page`
41
- * element. Defaults to `app`. When the view does not exist a minimal
42
- * built-in template is used.
40
+ * script element and mount div. Defaults to `app`. When the view does not
41
+ * exist a minimal built-in template is used.
43
42
  */
44
43
  root_view: string;
45
44
  /**
46
- * The id of the root DOM element the client mounts onto (the element that
47
- * carries the `data-page` attribute). Defaults to `app`.
45
+ * The id of the root DOM element the client mounts onto (and the `data-page`
46
+ * value on the JSON script element). Defaults to `app`.
48
47
  */
49
48
  root_id: string;
50
49
  /**
@@ -248,8 +247,15 @@ declare const resolveProps: (component: string, props: PageProps, request: Inert
248
247
  /** Escape a string for safe inclusion in a double-quoted HTML attribute. */
249
248
  declare const escapeHtmlAttribute: (value: string) => string;
250
249
  /**
251
- * Build the root mount element carrying the serialized page object in its
252
- * `data-page` attribute the element the Inertia client adapter hydrates.
250
+ * Build the elements the Inertia client adapter hydrates: the (empty) mount
251
+ * `<div id="…">` plus the JSON `<script type="application/json" data-page="…">`
252
+ * carrying the serialized page object.
253
+ *
254
+ * The client reads the initial page from the JSON script element
255
+ * (`script[data-page][type="application/json"]`), not from a `data-page`
256
+ * attribute on the mount div. Forward slashes are escaped to `\/` so an embedded
257
+ * `</script>` in the page payload cannot terminate the script element early
258
+ * (`\/` remains a valid JSON escape for `/`).
253
259
  */
254
260
  declare const renderDataPage: (page: InertiaPage, rootId: string) => string;
255
261
  /** Minimal built-in root document used when the configured root view is absent. */
@@ -265,23 +271,6 @@ declare const builtInTemplate: (mount: string, head?: string) => string;
265
271
  */
266
272
  declare const renderRootHtml: (page: InertiaPage, config: InertiaConfig) => Promise<string>;
267
273
  //#endregion
268
- //#region src/tags.d.ts
269
- /**
270
- * Register the `@inertia` and `@inertiaHead` Edge tags on a view factory.
271
- *
272
- * - `@inertia` renders the root mount element (`<div data-page="…">`), or the
273
- * server-rendered markup when SSR is enabled.
274
- * - `@inertiaHead` renders the SSR head tags (empty without SSR).
275
- *
276
- * Both read the values the adapter passes to the root template, so they replace
277
- * the equivalent `{{{ inertia }}}` / `{{{ inertiaHead }}}` interpolations.
278
- * Idempotent per factory.
279
- *
280
- * @param factory
281
- * @returns
282
- */
283
- declare const registerInertiaTags: (factory: ViewFactory) => void;
284
- //#endregion
285
274
  //#region src/ssr.d.ts
286
275
  /** The default address the Inertia SSR server listens on. */
287
276
  declare const DEFAULT_SSR_URL = "http://127.0.0.1:13714/render";
@@ -289,7 +278,7 @@ declare const DEFAULT_SSR_URL = "http://127.0.0.1:13714/render";
289
278
  interface SsrResponse {
290
279
  /** HTML strings to inject into the document `<head>` (title, meta, style). */
291
280
  head: string[];
292
- /** The rendered `<div id="app" data-page="…">…</div>` mount element. */
281
+ /** The rendered mount markup (JSON `<script data-page>` + server-rendered `<div>`). */
293
282
  body: string;
294
283
  }
295
284
  /**
@@ -392,4 +381,4 @@ declare const SEE_OTHER_METHODS: Set<string>;
392
381
  */
393
382
  declare const shouldUpgradeRedirect: (method: string, status: number) => boolean;
394
383
  //#endregion
395
- export { AlwaysProp, type AlwaysPropContract, DEFAULT_SSR_BUNDLE, DEFAULT_SSR_URL, DeferProp, type DeferPropContract, Inertia, type InertiaConfig, type InertiaPage, type InertiaPropWrapper, type InertiaRequest, type InertiaStore, LazyProp, type LazyPropContract, type PageProps, SEE_OTHER_METHODS, type SsrProcessController, type SsrResponse, type SuperviseOptions, builtInTemplate, configure, currentStore, defaultConfig, escapeHtmlAttribute, flushShared, inertia, inertiaConfig, isAlwaysProp, isDeferProp, isLazyProp, isPropWrapper, registerInertiaTags, renderDataPage, renderRootHtml, renderViaSsr, resetConfig, resolveProps, resolveSsrBundle, resolveVersion, runInertia, setVersion, shareData, sharedData, shouldUpgradeRedirect, superviseProcess };
384
+ export { AlwaysProp, type AlwaysPropContract, DEFAULT_SSR_BUNDLE, DEFAULT_SSR_URL, DeferProp, type DeferPropContract, Inertia, type InertiaConfig, type InertiaPage, type InertiaPropWrapper, type InertiaRequest, type InertiaStore, LazyProp, type LazyPropContract, type PageProps, SEE_OTHER_METHODS, type SsrProcessController, type SsrResponse, type SuperviseOptions, builtInTemplate, configure, currentStore, defaultConfig, escapeHtmlAttribute, flushShared, inertia, inertiaConfig, isAlwaysProp, isDeferProp, isLazyProp, isPropWrapper, renderDataPage, renderRootHtml, renderViaSsr, resetConfig, resolveProps, resolveSsrBundle, resolveVersion, runInertia, setVersion, shareData, sharedData, shouldUpgradeRedirect, superviseProcess };
package/dist/index.js CHANGED
@@ -1,18 +1,6 @@
1
1
  import { a as defaultConfig, c as resolveVersion, i as configure, l as setVersion, n as resolveSsrBundle, o as inertiaConfig, r as superviseProcess, s as resetConfig, t as DEFAULT_SSR_BUNDLE } from "./ssr-process-Cz4b8S_f.js";
2
2
  import { AsyncLocalStorage } from "node:async_hooks";
3
3
  import { Response } from "@arkstack/http";
4
- //#region \0rolldown/runtime.js
5
- var __defProp = Object.defineProperty;
6
- var __exportAll = (all, no_symbols) => {
7
- let target = {};
8
- for (var name in all) __defProp(target, name, {
9
- get: all[name],
10
- enumerable: true
11
- });
12
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
13
- return target;
14
- };
15
- //#endregion
16
4
  //#region src/props.ts
17
5
  /**
18
6
  * A prop that is excluded from the initial page load and only resolved when the
@@ -160,11 +148,18 @@ const escapeHtmlAttribute = (value) => {
160
148
  return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
161
149
  };
162
150
  /**
163
- * Build the root mount element carrying the serialized page object in its
164
- * `data-page` attribute the element the Inertia client adapter hydrates.
151
+ * Build the elements the Inertia client adapter hydrates: the (empty) mount
152
+ * `<div id="…">` plus the JSON `<script type="application/json" data-page="…">`
153
+ * carrying the serialized page object.
154
+ *
155
+ * The client reads the initial page from the JSON script element
156
+ * (`script[data-page][type="application/json"]`), not from a `data-page`
157
+ * attribute on the mount div. Forward slashes are escaped to `\/` so an embedded
158
+ * `<\/script>` in the page payload cannot terminate the script element early
159
+ * (`\/` remains a valid JSON escape for `/`).
165
160
  */
166
161
  const renderDataPage = (page, rootId) => {
167
- return `<div id="${rootId}" data-page="${escapeHtmlAttribute(JSON.stringify(page))}"></div>`;
162
+ return `<script data-page="${rootId}" type="application/json">${JSON.stringify(page).replace(/\//g, "\\/")}<\/script><div id="${rootId}"></div>`;
168
163
  };
169
164
  /** Minimal built-in root document used when the configured root view is absent. */
170
165
  const builtInTemplate = (mount, head = "") => {
@@ -203,7 +198,7 @@ const renderRootHtml = async (page, config) => {
203
198
  }
204
199
  try {
205
200
  const { view } = await import("@arkstack/view");
206
- const { registerInertiaTags } = await Promise.resolve().then(() => tags_exports);
201
+ const { registerInertiaTags } = await import("./tags-C-fCNM8D.js");
207
202
  const factory = view();
208
203
  registerInertiaTags(factory);
209
204
  if (factory.exists(config.root_view)) return await view(config.root_view, {
@@ -313,6 +308,8 @@ var Inertia = class Inertia {
313
308
  const request = Inertia.request();
314
309
  const config = inertiaConfig();
315
310
  const version = await resolveVersion();
311
+ const appName = globalThis.config?.("app.name");
312
+ if (appName !== void 0 && Inertia.shared().appName === void 0) Inertia.share("appName", appName);
316
313
  if (isInertiaRequest(request) && request.method === "GET" && (request.header("x-inertia-version") ?? "") !== version) return Inertia.conflict(request.url);
317
314
  const { props: resolved, deferredProps } = await resolveProps(component, {
318
315
  ...sharedData(),
@@ -406,42 +403,4 @@ function inertia(component, props = {}) {
406
403
  return Inertia.render(component, props);
407
404
  }
408
405
  //#endregion
409
- //#region src/tags.ts
410
- var tags_exports = /* @__PURE__ */ __exportAll({ registerInertiaTags: () => registerInertiaTags });
411
- /** Factories that already have the Inertia tags registered. */
412
- const registered = /* @__PURE__ */ new WeakSet();
413
- /**
414
- * Build an Edge tag compiler that outputs a template data variable raw (falling
415
- * back to an empty string when it is absent).
416
- *
417
- * @param name
418
- * @returns
419
- */
420
- const outputVariable = (name) => {
421
- return (parser, buffer, token) => {
422
- const ast = parser.utils.transformAst(parser.utils.generateAST(`${name} || ""`, token.loc, token.filename), token.filename, parser);
423
- buffer.outputExpression(parser.utils.stringify(ast), token.filename, token.loc.start.line, false);
424
- };
425
- };
426
- /**
427
- * Register the `@inertia` and `@inertiaHead` Edge tags on a view factory.
428
- *
429
- * - `@inertia` renders the root mount element (`<div data-page="…">`), or the
430
- * server-rendered markup when SSR is enabled.
431
- * - `@inertiaHead` renders the SSR head tags (empty without SSR).
432
- *
433
- * Both read the values the adapter passes to the root template, so they replace
434
- * the equivalent `{{{ inertia }}}` / `{{{ inertiaHead }}}` interpolations.
435
- * Idempotent per factory.
436
- *
437
- * @param factory
438
- * @returns
439
- */
440
- const registerInertiaTags = (factory) => {
441
- if (registered.has(factory)) return;
442
- registered.add(factory);
443
- factory.tag("inertia", false, false, outputVariable("inertia"));
444
- factory.tag("inertiaHead", false, false, outputVariable("inertiaHead"));
445
- };
446
- //#endregion
447
- export { AlwaysProp, DEFAULT_SSR_BUNDLE, DEFAULT_SSR_URL, DeferProp, Inertia, LazyProp, SEE_OTHER_METHODS, builtInTemplate, configure, currentStore, defaultConfig, escapeHtmlAttribute, flushShared, inertia, inertiaConfig, isAlwaysProp, isDeferProp, isLazyProp, isPropWrapper, registerInertiaTags, renderDataPage, renderRootHtml, renderViaSsr, resetConfig, resolveProps, resolveSsrBundle, resolveVersion, runInertia, setVersion, shareData, sharedData, shouldUpgradeRedirect, superviseProcess };
406
+ export { AlwaysProp, DEFAULT_SSR_BUNDLE, DEFAULT_SSR_URL, DeferProp, Inertia, LazyProp, SEE_OTHER_METHODS, builtInTemplate, configure, currentStore, defaultConfig, escapeHtmlAttribute, flushShared, inertia, inertiaConfig, isAlwaysProp, isDeferProp, isLazyProp, isPropWrapper, renderDataPage, renderRootHtml, renderViaSsr, resetConfig, resolveProps, resolveSsrBundle, resolveVersion, runInertia, setVersion, shareData, sharedData, shouldUpgradeRedirect, superviseProcess };
package/dist/setup.js CHANGED
@@ -16,6 +16,9 @@ Publisher.publishes({
16
16
  entries: [{
17
17
  from: join(root, "stubs/config/inertia.ts.stub"),
18
18
  to: "src/config/inertia.ts"
19
+ }, {
20
+ from: join(root, "stubs/config/app.css.stub"),
21
+ to: "resources/css/app.css"
19
22
  }]
20
23
  });
21
24
  Publisher.publishes({
@@ -26,5 +29,98 @@ Publisher.publishes({
26
29
  to: "src/resources/views/app.edge"
27
30
  }]
28
31
  });
32
+ Publisher.publishes({
33
+ package: "@arkstack/inertia",
34
+ tag: "inertia-react",
35
+ entries: [
36
+ {
37
+ from: join(root, "stubs/react/resources/js/app.tsx.stub"),
38
+ to: "resources/js/app.tsx"
39
+ },
40
+ {
41
+ from: join(root, "stubs/react/vite.config.ts.stub"),
42
+ to: "vite.config.ts"
43
+ },
44
+ {
45
+ from: join(root, "stubs/react/resources/js/Pages/Index.tsx.stub"),
46
+ to: "resources/js/Pages/Index.tsx"
47
+ },
48
+ {
49
+ from: join(root, "stubs/shared/resources/js/vite-env.d.ts.stub"),
50
+ to: "resources/js/vite-env.d.ts"
51
+ }
52
+ ]
53
+ });
54
+ Publisher.publishes({
55
+ package: "@arkstack/inertia",
56
+ tag: "inertia-svelte",
57
+ entries: [
58
+ {
59
+ from: join(root, "stubs/svelte/resources/js/app.ts.stub"),
60
+ to: "resources/js/app.ts"
61
+ },
62
+ {
63
+ from: join(root, "stubs/svelte/vite.config.ts.stub"),
64
+ to: "vite.config.ts"
65
+ },
66
+ {
67
+ from: join(root, "stubs/svelte/resources/js/Pages/Index.svelte.stub"),
68
+ to: "resources/js/Pages/Index.svelte"
69
+ },
70
+ {
71
+ from: join(root, "stubs/shared/resources/js/vite-env.d.ts.stub"),
72
+ to: "resources/js/vite-env.d.ts"
73
+ }
74
+ ]
75
+ });
76
+ Publisher.publishes({
77
+ package: "@arkstack/inertia",
78
+ tag: "inertia-vue",
79
+ entries: [
80
+ {
81
+ from: join(root, "stubs/vue/resources/js/app.ts.stub"),
82
+ to: "resources/js/app.ts"
83
+ },
84
+ {
85
+ from: join(root, "stubs/vue/vite.config.ts.stub"),
86
+ to: "vite.config.ts"
87
+ },
88
+ {
89
+ from: join(root, "stubs/vue/resources/js/Pages/Index.vue.stub"),
90
+ to: "resources/js/Pages/Index.vue"
91
+ },
92
+ {
93
+ from: join(root, "stubs/shared/resources/js/vite-env.d.ts.stub"),
94
+ to: "resources/js/vite-env.d.ts"
95
+ }
96
+ ]
97
+ });
98
+ Publisher.confirm({
99
+ package: "@arkstack/inertia",
100
+ message: "What front-end framework are you using with Inertia?",
101
+ options: [
102
+ {
103
+ name: "React",
104
+ value: "inertia-react"
105
+ },
106
+ {
107
+ name: "Svelte",
108
+ value: "inertia-svelte"
109
+ },
110
+ {
111
+ name: "Vue",
112
+ value: "inertia-vue"
113
+ }
114
+ ],
115
+ callback: (tag, stub) => {
116
+ const exts = {
117
+ "inertia-react": "tsx",
118
+ "inertia-svelte": "ts",
119
+ "inertia-vue": "ts"
120
+ };
121
+ const reactRefresh = tag === "inertia-react" ? "@viteReactRefresh\n " : "";
122
+ return stub.replaceAll("{{ext}}", exts[tag] ?? "ts").replaceAll("{{reactRefresh}}", reactRefresh);
123
+ }
124
+ });
29
125
  //#endregion
30
126
  export {};
@@ -0,0 +1,38 @@
1
+ //#region src/tags.ts
2
+ /** Factories that already have the Inertia tags registered. */
3
+ const registered = /* @__PURE__ */ new WeakSet();
4
+ /**
5
+ * Build an Edge tag compiler that outputs a template data variable raw (falling
6
+ * back to an empty string when it is absent).
7
+ *
8
+ * @param name
9
+ * @returns
10
+ */
11
+ const outputVariable = (name) => {
12
+ return (parser, buffer, token) => {
13
+ const ast = parser.utils.transformAst(parser.utils.generateAST(`${name} || ""`, token.loc, token.filename), token.filename, parser);
14
+ buffer.outputExpression(parser.utils.stringify(ast), token.filename, token.loc.start.line, false);
15
+ };
16
+ };
17
+ /**
18
+ * Register the `@inertia` and `@inertiaHead` Edge tags on a view factory.
19
+ *
20
+ * - `@inertia` renders the root mount element (the JSON `<script data-page="…">`
21
+ * plus the mount `<div>`), or the server-rendered markup when SSR is enabled.
22
+ * - `@inertiaHead` renders the SSR head tags (empty without SSR).
23
+ *
24
+ * Both read the values the adapter passes to the root template, so they replace
25
+ * the equivalent `{{{ inertia }}}` / `{{{ inertiaHead }}}` interpolations.
26
+ * Idempotent per factory.
27
+ *
28
+ * @param factory
29
+ * @returns
30
+ */
31
+ const registerInertiaTags = (factory) => {
32
+ if (registered.has(factory)) return;
33
+ registered.add(factory);
34
+ factory.tag("inertia", false, false, outputVariable("inertia"));
35
+ factory.tag("inertiaHead", false, false, outputVariable("inertiaHead"));
36
+ };
37
+ //#endregion
38
+ export { registerInertiaTags };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkstack/inertia",
3
- "version": "0.15.5",
3
+ "version": "0.16.0",
4
4
  "type": "module",
5
5
  "description": "InertiaJS server-side adapter for Arkstack, building the modern monolith with server-driven SPAs.",
6
6
  "homepage": "https://arkstack.toneflix.net/guide/inertia",
@@ -33,13 +33,13 @@
33
33
  "./package.json": "./package.json"
34
34
  },
35
35
  "dependencies": {
36
- "@arkstack/common": "^0.15.5"
36
+ "@arkstack/common": "^0.16.0"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@h3ravel/musket": "^2.2.1",
40
- "@arkstack/contract": "^0.15.5",
41
- "@arkstack/view": "^0.15.5",
42
- "@arkstack/http": "^0.15.5"
40
+ "@arkstack/http": "^0.16.0",
41
+ "@arkstack/contract": "^0.16.0",
42
+ "@arkstack/view": "^0.16.0"
43
43
  },
44
44
  "peerDependenciesMeta": {
45
45
  "@arkstack/view": {
@@ -0,0 +1,343 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ margin: 0;
6
+ padding: 0;
7
+ }
8
+
9
+ /* ── Dark theme (default) ── */
10
+ :root {
11
+ --gold-light: #f0c040;
12
+ --gold-mid: #c9960c;
13
+ --gold-dark: #8a6400;
14
+ --bg: #0b0c0e;
15
+ --bg-card: #111317;
16
+ --bg-snippet: #0d0f12;
17
+ --border: rgba(201, 150, 12, 0.18);
18
+ --text: #f5f0e8;
19
+ --muted: #7a7567;
20
+ --radius: 10px;
21
+ --card-hover: #161820;
22
+ --kw: #8a9ad6;
23
+ --fn: #e8c56a;
24
+ --str: #88c99a;
25
+ --cm: #4a5060;
26
+ --code-base: #d4b86a;
27
+ }
28
+
29
+ /* ── Light theme ── */
30
+ [data-theme="light"] {
31
+ --gold-light: #a06800;
32
+ --gold-mid: #8a5a00;
33
+ --gold-dark: #6b4400;
34
+ --bg: #f5f3ef;
35
+ --bg-card: #ffffff;
36
+ --bg-snippet: #f0ede8;
37
+ --border: rgba(160, 104, 0, 0.16);
38
+ --text: #1a1714;
39
+ --muted: #9a9080;
40
+ --card-hover: #f9f7f3;
41
+ --kw: #5460a0;
42
+ --fn: #8a6000;
43
+ --str: #2e7d52;
44
+ --cm: #aaa090;
45
+ --code-base: #7a5c20;
46
+ }
47
+
48
+ /* ── OS preference fallback (no JS) ── */
49
+ @media (prefers-color-scheme: light) {
50
+ :root:not([data-theme="dark"]) {
51
+ --gold-light: #a06800;
52
+ --gold-mid: #8a5a00;
53
+ --gold-dark: #6b4400;
54
+ --bg: #f5f3ef;
55
+ --bg-card: #ffffff;
56
+ --bg-snippet: #f0ede8;
57
+ --border: rgba(160, 104, 0, 0.16);
58
+ --text: #1a1714;
59
+ --muted: #9a9080;
60
+ --card-hover: #f9f7f3;
61
+ --kw: #5460a0;
62
+ --fn: #8a6000;
63
+ --str: #2e7d52;
64
+ --cm: #aaa090;
65
+ --code-base: #7a5c20;
66
+ }
67
+ }
68
+
69
+ html,
70
+ body {
71
+ min-height: 100vh;
72
+ background: var(--bg);
73
+ color: var(--text);
74
+ font-family: 'DM Mono', monospace;
75
+ display: flex;
76
+ align-items: center;
77
+ justify-content: center;
78
+ padding: 2rem 1rem;
79
+ transition: background 0.25s, color 0.25s;
80
+ }
81
+
82
+ .page {
83
+ max-width: 580px;
84
+ width: 100%;
85
+ display: flex;
86
+ flex-direction: column;
87
+ align-items: center;
88
+ }
89
+
90
+ /* ── Top bar ── */
91
+ .topbar {
92
+ width: 100%;
93
+ display: flex;
94
+ align-items: center;
95
+ justify-content: space-between;
96
+ margin-bottom: 2.8rem;
97
+ }
98
+
99
+ /* ── Brand block ── */
100
+ .brand {
101
+ display: flex;
102
+ align-items: center;
103
+ gap: 18px;
104
+ }
105
+
106
+ .logo-wrap {
107
+ position: relative;
108
+ flex-shrink: 0;
109
+ }
110
+
111
+ .logo-wrap img {
112
+ width: 55px;
113
+ height: 55px;
114
+ display: block;
115
+ filter: drop-shadow(0 0 18px rgba(201, 150, 12, 0.35));
116
+ animation: floatLogo 4s ease-in-out infinite;
117
+ }
118
+
119
+ .halo {
120
+ position: absolute;
121
+ inset: -14px;
122
+ border-radius: 50%;
123
+ background: radial-gradient(circle, rgba(201, 150, 12, 0.1) 0%, transparent 70%);
124
+ pointer-events: none;
125
+ animation: pulse 4s ease-in-out infinite;
126
+ }
127
+
128
+ @keyframes floatLogo {
129
+
130
+ 0%,
131
+ 100% {
132
+ transform: translateY(0);
133
+ }
134
+
135
+ 50% {
136
+ transform: translateY(-5px);
137
+ }
138
+ }
139
+
140
+ @keyframes pulse {
141
+
142
+ 0%,
143
+ 100% {
144
+ opacity: 0.5;
145
+ transform: scale(1);
146
+ }
147
+
148
+ 50% {
149
+ opacity: 1;
150
+ transform: scale(1.08);
151
+ }
152
+ }
153
+
154
+ .brand-text {
155
+ display: flex;
156
+ flex-direction: column;
157
+ gap: 4px;
158
+ }
159
+
160
+ .wordmark {
161
+ font-family: 'Syne', sans-serif;
162
+ font-size: 2.5rem;
163
+ font-weight: 800;
164
+ letter-spacing: -0.03em;
165
+ color: var(--text);
166
+ line-height: 1;
167
+ }
168
+
169
+ .wordmark span {
170
+ color: var(--gold-light);
171
+ }
172
+
173
+ .tagline {
174
+ font-size: 0.75rem;
175
+ letter-spacing: 0.14em;
176
+ text-transform: uppercase;
177
+ color: var(--muted);
178
+ line-height: 1;
179
+ }
180
+
181
+ /* ── Status pill ── */
182
+ .status-row {
183
+ display: flex;
184
+ align-items: center;
185
+ gap: 8px;
186
+ background: rgba(201, 150, 12, 0.06);
187
+ border: 1px solid var(--border);
188
+ border-radius: 100px;
189
+ padding: 6px 16px 6px 10px;
190
+ font-size: 0.72rem;
191
+ letter-spacing: 0.06em;
192
+ color: var(--gold-light);
193
+ margin-top: 1rem;
194
+ margin-bottom: 2.8rem;
195
+ transition: border-color 0.25s;
196
+ }
197
+
198
+ .dot {
199
+ width: 7px;
200
+ height: 7px;
201
+ border-radius: 50%;
202
+ background: #4caf74;
203
+ box-shadow: 0 0 6px #4caf74;
204
+ animation: blink 2s ease-in-out infinite;
205
+ }
206
+
207
+ @keyframes blink {
208
+
209
+ 0%,
210
+ 100% {
211
+ opacity: 1;
212
+ }
213
+
214
+ 50% {
215
+ opacity: 0.4;
216
+ }
217
+ }
218
+
219
+ /* ── Divider ── */
220
+ .divider {
221
+ width: 100%;
222
+ height: 1px;
223
+ background: linear-gradient(90deg, transparent, var(--border), transparent);
224
+ margin-bottom: 2.8rem;
225
+ }
226
+
227
+ /* ── Feature cards ── */
228
+ .cards {
229
+ width: 100%;
230
+ display: grid;
231
+ grid-template-columns: 1fr 1fr 1fr;
232
+ gap: 12px;
233
+ margin-bottom: 2.4rem;
234
+ }
235
+
236
+ .card {
237
+ background: var(--bg-card);
238
+ border: 1px solid var(--border);
239
+ border-radius: var(--radius);
240
+ padding: 1.1rem 1rem;
241
+ display: flex;
242
+ flex-direction: column;
243
+ gap: 6px;
244
+ transition: border-color 0.2s, background 0.2s;
245
+ }
246
+
247
+ .card:hover {
248
+ border-color: var(--gold-mid);
249
+ background: var(--card-hover);
250
+ }
251
+
252
+ .card-icon {
253
+ font-size: 1.1rem;
254
+ margin-bottom: 4px;
255
+ opacity: 0.85;
256
+ width: 20px;
257
+ }
258
+
259
+ .card-title {
260
+ font-family: 'Syne', sans-serif;
261
+ font-weight: 700;
262
+ font-size: 0.82rem;
263
+ color: var(--text);
264
+ }
265
+
266
+ .card-desc {
267
+ font-size: 0.68rem;
268
+ color: var(--muted);
269
+ line-height: 1.5;
270
+ }
271
+
272
+ /* ── Code snippet ── */
273
+ .snippet {
274
+ width: 100%;
275
+ background: var(--bg-snippet);
276
+ border: 1px solid var(--border);
277
+ border-radius: var(--radius);
278
+ padding: 1.1rem 1.25rem;
279
+ margin-bottom: 2.4rem;
280
+ position: relative;
281
+ overflow: hidden;
282
+ transition: background 0.25s, border-color 0.25s;
283
+ }
284
+
285
+ .snippet::before {
286
+ content: '';
287
+ position: absolute;
288
+ top: 0;
289
+ left: 0;
290
+ right: 0;
291
+ height: 2px;
292
+ background: linear-gradient(90deg, var(--gold-dark), var(--gold-light), var(--gold-dark));
293
+ }
294
+
295
+ .snippet-label {
296
+ font-size: 0.65rem;
297
+ letter-spacing: 0.12em;
298
+ text-transform: uppercase;
299
+ color: var(--muted);
300
+ margin-bottom: 0.7rem;
301
+ }
302
+
303
+ .snippet code {
304
+ font-family: 'DM Mono', monospace;
305
+ font-size: 0.78rem;
306
+ color: var(--code-base);
307
+ display: block;
308
+ line-height: 1.9;
309
+ }
310
+
311
+ .kw {
312
+ color: var(--kw);
313
+ }
314
+
315
+ .fn {
316
+ color: var(--fn);
317
+ }
318
+
319
+ .str {
320
+ color: var(--str);
321
+ }
322
+
323
+ .cm {
324
+ color: var(--cm);
325
+ }
326
+
327
+ /* ── Footer ── */
328
+ .footer {
329
+ font-size: 0.65rem;
330
+ color: var(--muted);
331
+ text-align: center;
332
+ letter-spacing: 0.06em;
333
+ line-height: 2;
334
+ }
335
+
336
+ .footer a {
337
+ color: var(--gold-mid);
338
+ text-decoration: none;
339
+ }
340
+
341
+ .footer a:hover {
342
+ color: var(--gold-light);
343
+ }
@@ -0,0 +1,7 @@
1
+ export default function Index({appName}: {appName: string}) {
2
+ return (
3
+ <div>
4
+ <h1>Hello, {appName}!</h1>
5
+ </div>
6
+ );
7
+ }
@@ -0,0 +1,14 @@
1
+ import { createInertiaApp } from '@inertiajs/react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import type { ComponentType } from 'react';
4
+
5
+ createInertiaApp({
6
+ resolve: (name) => {
7
+ const pages = import.meta.glob<{ default: ComponentType }>('./Pages/**/*.tsx', { eager: true });
8
+
9
+ return pages[`./Pages/${name}.tsx`];
10
+ },
11
+ setup({ el, App, props }) {
12
+ createRoot(el).render(<App {...props} />);
13
+ },
14
+ });
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ build: {
7
+ manifest: true,
8
+ outDir: 'public/build',
9
+ rolldownOptions: { input: 'resources/js/app.tsx' },
10
+ },
11
+ })
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,7 @@
1
+ <script lang="ts">
2
+ export let appName: string;
3
+ </script>
4
+
5
+ <div>
6
+ <h1>Hello, {appName}!</h1>
7
+ </div>
@@ -0,0 +1,13 @@
1
+ import { createInertiaApp } from '@inertiajs/svelte'
2
+ import { mount } from 'svelte'
3
+
4
+ createInertiaApp({
5
+ resolve: (name) => {
6
+ const pages = import.meta.glob('./Pages/**/*.svelte', { eager: true });
7
+
8
+ return pages[`./Pages/${name}.svelte`];
9
+ },
10
+ setup({ el, App, props }) {
11
+ mount(App, { target: el, props });
12
+ },
13
+ })
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'vite'
2
+ import { svelte } from '@sveltejs/vite-plugin-svelte'
3
+
4
+ export default defineConfig({
5
+ plugins: [svelte()],
6
+ build: {
7
+ manifest: true,
8
+ outDir: 'public/build',
9
+ rolldownOptions: { input: 'resources/js/app.ts' },
10
+ },
11
+ })
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  @inertiaHead
7
- @vite('resources/js/app.ts')
7
+ {{reactRefresh}}@vite(['resources/js/app.{{ext}}', 'resources/css/app.css'])
8
8
  </head>
9
9
  <body>
10
10
  @inertia
@@ -0,0 +1,11 @@
1
+ <script setup lang="ts">
2
+ defineProps<{
3
+ appName: string
4
+ }>()
5
+ </script>
6
+
7
+ <template>
8
+ <div>
9
+ <h1>Hello, {{ appName }}!</h1>
10
+ </div>
11
+ </template>
@@ -0,0 +1,15 @@
1
+ import { createApp, h } from 'vue'
2
+ import { createInertiaApp } from '@inertiajs/vue3'
3
+
4
+ createInertiaApp({
5
+ resolve: (name) => {
6
+ const pages = import.meta.glob('./Pages/**/*.vue', { eager: true });
7
+
8
+ return pages[`./Pages/${name}.vue`];
9
+ },
10
+ setup({ el, App, props, plugin }) {
11
+ createApp({ render: () => h(App, props) })
12
+ .use(plugin)
13
+ .mount(el);
14
+ },
15
+ })
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'vite'
2
+ import vue from '@vitejs/plugin-vue'
3
+
4
+ export default defineConfig({
5
+ plugins: [vue()],
6
+ build: {
7
+ manifest: true,
8
+ outDir: 'public/build',
9
+ rolldownOptions: { input: 'resources/js/app.ts' },
10
+ },
11
+ })