create-caspian-app 1.6.4 → 1.8.1

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.
@@ -1,95 +1,94 @@
1
- from typing import Optional
2
- from casp.component_decorator import html
3
- from casp.layout import Metadata
4
-
5
-
6
- metadata = Metadata(
7
- title="Application Error",
8
- description="An unexpected error occurred.",
9
- extra={"robots": "noindex, nofollow"},
10
- )
11
-
12
-
13
- def page(error_message: str, error_trace: Optional[str] = None):
14
- return html(r"""
15
- <main class="container py-10">
16
- <div class="mx-auto max-w-2xl rounded bg-white p-6 shadow-sm">
17
- <div class="flex items-start gap-4">
18
- <svg class="h-10 w-10 text-red-600"
19
- viewBox="0 0 24 24"
20
- fill="none"
21
- xmlns="http://www.w3.org/2000/svg"
22
- aria-hidden="true">
23
- <path d="M11.001 2a1 1 0 0 1 .998 0L21 7v9a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V7l8.001-5z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
24
- <path d="M12 8v4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
25
- <path d="M12 16h.01" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
26
- </svg>
27
-
28
- <div class="flex-1">
29
- <h1 class="text-2xl font-semibold text-red-600">Something went wrong</h1>
30
- <p class="mt-2 text-sm text-gray-600">
31
- We're sorry—an unexpected error occurred. Try reloading or return to
32
- the homepage.
33
- </p>
34
- <p class="mt-3 text-sm text-gray-700">{{ error_message }}</p>
35
-
36
- <div class="mt-4 flex flex-wrap gap-2">
37
- <a href="/"
38
- class="inline-block rounded border border-gray-200 bg-gray-100 px-3 py-1.5 text-sm text-gray-800 hover:bg-gray-200">
39
- Home
40
- </a>
41
- <button class="inline-block rounded bg-blue-600 px-3 py-1.5 text-sm text-white"
42
- onclick="window.location.reload()">Reload</button>
43
- {% if error_trace %}
44
- <button class="inline-block rounded border border-gray-200 bg-transparent px-3 py-1.5 text-sm text-black"
45
- onclick="setShowTrace(current => !current)">{showTrace ? "Hide details" : "Show details"}</button>
46
- {% endif %}
47
- </div>
48
- </div>
49
- </div>
50
-
51
- {% if error_trace %}
52
- <section class="mt-4" hidden="{!showTrace}">
53
- <div class="mb-2 flex items-center justify-between">
54
- <div class="text-xs text-gray-500">Error details (development only)</div>
55
- <div class="flex gap-2">
56
- <button class="rounded border bg-gray-100 px-2 py-1 text-xs text-black"
57
- onclick="copyTrace()">{traceCopied ? "Copied" : "Copy"}</button>
58
- <button class="rounded border bg-gray-100 px-2 py-1 text-xs text-black"
59
- onclick="downloadTrace()">Download</button>
60
- </div>
61
- </div>
62
- <pre pp-ref="{tracePre}"
63
- class="overflow-auto rounded bg-gray-100 p-4 text-xs text-black">{{ error_trace }}</pre>
64
- </section>
65
- {% endif %}
66
-
67
- <script>
68
- const [showTrace, setShowTrace] = pp.state(false);
69
- const [traceCopied, setTraceCopied] = pp.state(false);
70
- const tracePre = pp.ref(null);
71
-
72
- async function copyTrace() {
73
- if (!tracePre.current) return;
74
- await navigator.clipboard.writeText(tracePre.current.innerText);
75
- setTraceCopied(true);
76
- setTimeout(() => setTraceCopied(false), 2000);
77
- }
78
-
79
- function downloadTrace() {
80
- if (!tracePre.current) return;
81
- const blob = new Blob([tracePre.current.innerText], { type: "text/plain" });
82
- const url = URL.createObjectURL(blob);
83
- const link = document.createElement("a");
84
- link.href = url;
85
- link.download = "error_trace.txt";
86
- link.click();
87
- URL.revokeObjectURL(url);
88
- }
89
- </script>
90
- </div>
91
- </main>
92
- """,
93
- error_message=error_message,
94
- error_trace=error_trace,
95
- )
1
+ from casp.component_decorator import html
2
+ from casp.errors import ErrorInfo
3
+ from casp.layout import Metadata
4
+
5
+
6
+ metadata = Metadata(
7
+ title="Application Error",
8
+ description="An unexpected error occurred.",
9
+ extra={"robots": "noindex, nofollow"},
10
+ )
11
+
12
+
13
+ def page(error: ErrorInfo):
14
+ return html(r"""
15
+ <main class="container py-10">
16
+ <div class="mx-auto max-w-2xl rounded bg-white p-6 shadow-sm">
17
+ <div class="flex items-start gap-4">
18
+ <svg class="h-10 w-10 text-red-600"
19
+ viewBox="0 0 24 24"
20
+ fill="none"
21
+ xmlns="http://www.w3.org/2000/svg"
22
+ aria-hidden="true">
23
+ <path d="M11.001 2a1 1 0 0 1 .998 0L21 7v9a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V7l8.001-5z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
24
+ <path d="M12 8v4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
25
+ <path d="M12 16h.01" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
26
+ </svg>
27
+
28
+ <div class="flex-1">
29
+ <p class="text-xs font-medium uppercase tracking-wide text-gray-500">{{ error.status }} · {{ error.reason }}</p>
30
+ <h1 class="mt-1 text-2xl font-semibold text-red-600">Something went wrong</h1>
31
+ <p class="mt-3 text-sm text-gray-700">{{ error.message }}</p>
32
+ {% if error.request_id %}
33
+ <p class="mt-2 text-xs text-gray-500">Reference: {{ error.request_id }}</p>
34
+ {% endif %}
35
+
36
+ <div class="mt-4 flex flex-wrap gap-2">
37
+ <a href="/"
38
+ class="inline-block rounded border border-gray-200 bg-gray-100 px-3 py-1.5 text-sm text-gray-800 hover:bg-gray-200">
39
+ Home
40
+ </a>
41
+ <button class="inline-block rounded bg-blue-600 px-3 py-1.5 text-sm text-white"
42
+ onclick="window.location.reload()">Reload</button>
43
+ {% if error.trace %}
44
+ <button class="inline-block rounded border border-gray-200 bg-transparent px-3 py-1.5 text-sm text-black"
45
+ onclick="setShowTrace(current => !current)">{showTrace ? "Hide details" : "Show details"}</button>
46
+ {% endif %}
47
+ </div>
48
+ </div>
49
+ </div>
50
+
51
+ {% if error.trace %}
52
+ <section class="mt-4" hidden="{!showTrace}">
53
+ <div class="mb-2 flex items-center justify-between">
54
+ <div class="text-xs text-gray-500">Error details (development only)</div>
55
+ <div class="flex gap-2">
56
+ <button class="rounded border bg-gray-100 px-2 py-1 text-xs text-black"
57
+ onclick="copyTrace()">{traceCopied ? "Copied" : "Copy"}</button>
58
+ <button class="rounded border bg-gray-100 px-2 py-1 text-xs text-black"
59
+ onclick="downloadTrace()">Download</button>
60
+ </div>
61
+ </div>
62
+ <pre pp-ref="{tracePre}"
63
+ class="overflow-auto rounded bg-gray-100 p-4 text-xs text-black">{{ error.trace }}</pre>
64
+ </section>
65
+ {% endif %}
66
+
67
+ <script>
68
+ const [showTrace, setShowTrace] = pp.state(false);
69
+ const [traceCopied, setTraceCopied] = pp.state(false);
70
+ const tracePre = pp.ref(null);
71
+
72
+ async function copyTrace() {
73
+ if (!tracePre.current) return;
74
+ await navigator.clipboard.writeText(tracePre.current.innerText);
75
+ setTraceCopied(true);
76
+ setTimeout(() => setTraceCopied(false), 2000);
77
+ }
78
+
79
+ function downloadTrace() {
80
+ if (!tracePre.current) return;
81
+ const blob = new Blob([tracePre.current.innerText], { type: "text/plain" });
82
+ const url = URL.createObjectURL(blob);
83
+ const link = document.createElement("a");
84
+ link.href = url;
85
+ link.download = "error_trace.txt";
86
+ link.click();
87
+ URL.revokeObjectURL(url);
88
+ }
89
+ </script>
90
+ </div>
91
+ </main>
92
+ """,
93
+ error=error,
94
+ )
@@ -1,47 +1,43 @@
1
- from casp.component_decorator import html
2
-
3
-
4
- def layout():
5
- return html(r"""
6
- <!DOCTYPE html>
7
- <html lang="en">
8
-
9
- <head>
10
- <meta charset="UTF-8" />
11
- <meta name="viewport"
12
- content="width=device-width, initial-scale=1.0" />
13
- <title>{{ metadata.title | default("Caspian App") }}</title>
14
- <meta name="description"
15
- content="{{ metadata.description | default('Powered by Caspian Framework') }}" />
16
- {#
17
- Everything a page put in `Metadata(extra={...})` -- robots, canonical,
18
- Open Graph, Twitter cards. The page pipeline already merges `extra` into
19
- this dict; without this loop it collected those values and dropped them,
20
- so `extra` silently did nothing. Open Graph is keyed by `property`, every
21
- other vocabulary by `name`.
22
- #}
23
- {% for meta_name, meta_content in (metadata | default({})).items()
24
- if meta_name not in ("title", "description") and meta_content %}
25
- {% if meta_name.startswith("og:") %}
26
- <meta property="{{ meta_name }}" content="{{ meta_content }}" />
27
- {% else %}
28
- <meta name="{{ meta_name }}" content="{{ meta_content }}" />
29
- {% endif %}
30
- {% endfor %}
31
- <link rel="icon"
32
- href="/favicon.ico"
33
- type="image/x-icon"
34
- sizes="16x16" />
35
- </head>
36
-
37
- <body style="
38
- opacity: 0;
39
- pointer-events: none;
40
- user-select: none;
41
- transition: opacity 0.18s ease-out;
42
- ">
43
- <slot />
44
- </body>
45
-
46
- </html>
47
- """)
1
+ from casp.component_decorator import html
2
+ from casp.layout import Metadata
3
+
4
+ # Root defaults, inherited by every page unless overridden. A page title can be
5
+ # formatted for the whole app with `title_template="%s | My App"`.
6
+ metadata = Metadata(description="Powered by Caspian Framework")
7
+
8
+
9
+ def layout():
10
+ return html(r"""
11
+ <!DOCTYPE html>
12
+ <html lang="{{ current_locale() }}">
13
+
14
+ <head>
15
+ <meta charset="UTF-8" />
16
+ <meta name="viewport"
17
+ content="width=device-width, initial-scale=1.0" />
18
+ <title>{{ metadata.title | default("Caspian App") }}</title>
19
+ {#
20
+ Description, canonical, robots, keywords, Open Graph, Twitter, and every
21
+ `Metadata(extra={...})` entry. Each tag carries `data-pp-meta` so SPA
22
+ navigation replaces the set for the next page.
23
+ #}
24
+ {{ metadata_tags(metadata | default({})) }}
25
+ <link rel="icon"
26
+ href="/favicon.ico"
27
+ type="image/x-icon"
28
+ sizes="16x16" />
29
+ <link href="/css/styles.css" rel="stylesheet" />
30
+ <script type="module" src="/js/main.js"></script>
31
+ </head>
32
+
33
+ <body style="
34
+ opacity: 0;
35
+ pointer-events: none;
36
+ user-select: none;
37
+ transition: opacity 0.18s ease-out;
38
+ ">
39
+ <slot />
40
+ </body>
41
+
42
+ </html>
43
+ """)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-caspian-app",
3
- "version": "1.6.4",
3
+ "version": "1.8.1",
4
4
  "description": "Scaffold a new Caspian project (FastAPI-powered reactive Python framework).",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",