create-caspian-app 1.0.22 → 1.2.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/README.md +115 -95
- package/dist/.github/copilot-instructions.md +24 -25
- package/dist/AGENTS.md +20 -20
- package/dist/index.js +1 -1
- package/dist/main.py +304 -267
- package/dist/public/js/pp-reactive-v2.min.js +1 -1
- package/dist/src/app/error.py +113 -0
- package/dist/src/app/{index.html → index.py} +6 -0
- package/dist/src/app/layout.py +32 -0
- package/dist/src/app/{not-found.html → not_found.py} +32 -18
- package/dist/tests/README.md +1 -1
- package/package.json +2 -2
- package/dist/src/app/error.html +0 -111
- package/dist/src/app/layout.html +0 -26
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from casp.component_decorator import html
|
|
4
|
+
from casp.layout import Metadata
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
metadata = Metadata(
|
|
8
|
+
title="Application Error",
|
|
9
|
+
description="An unexpected error occurred.",
|
|
10
|
+
extra={"robots": "noindex, nofollow"},
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def page(error_message: str, error_trace: Optional[str] = None):
|
|
15
|
+
return html(r"""
|
|
16
|
+
<main class="container py-10">
|
|
17
|
+
<div class="mx-auto max-w-2xl rounded bg-white p-6 shadow-sm">
|
|
18
|
+
<div class="flex items-start gap-4">
|
|
19
|
+
<svg class="h-10 w-10 text-red-600"
|
|
20
|
+
viewBox="0 0 24 24"
|
|
21
|
+
fill="none"
|
|
22
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
23
|
+
aria-hidden="true">
|
|
24
|
+
<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"
|
|
25
|
+
stroke="currentColor"
|
|
26
|
+
stroke-width="1.5"
|
|
27
|
+
stroke-linecap="round"
|
|
28
|
+
stroke-linejoin="round" />
|
|
29
|
+
<path d="M12 8v4"
|
|
30
|
+
stroke="currentColor"
|
|
31
|
+
stroke-width="1.5"
|
|
32
|
+
stroke-linecap="round"
|
|
33
|
+
stroke-linejoin="round" />
|
|
34
|
+
<path d="M12 16h.01"
|
|
35
|
+
stroke="currentColor"
|
|
36
|
+
stroke-width="1.5"
|
|
37
|
+
stroke-linecap="round"
|
|
38
|
+
stroke-linejoin="round" />
|
|
39
|
+
</svg>
|
|
40
|
+
|
|
41
|
+
<div class="flex-1">
|
|
42
|
+
<h1 class="text-2xl font-semibold text-red-600">Something went wrong</h1>
|
|
43
|
+
<p class="mt-2 text-sm text-gray-600">
|
|
44
|
+
We're sorry—an unexpected error occurred. Try reloading or return to
|
|
45
|
+
the homepage.
|
|
46
|
+
</p>
|
|
47
|
+
<p class="mt-3 text-sm text-gray-700">{{ error_message }}</p>
|
|
48
|
+
|
|
49
|
+
<div class="mt-4 flex flex-wrap gap-2">
|
|
50
|
+
<a href="/"
|
|
51
|
+
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">
|
|
52
|
+
Home
|
|
53
|
+
</a>
|
|
54
|
+
<button class="inline-block rounded bg-blue-600 px-3 py-1.5 text-sm text-white"
|
|
55
|
+
onclick="window.location.reload()">
|
|
56
|
+
Reload
|
|
57
|
+
</button>
|
|
58
|
+
{% if error_trace %}
|
|
59
|
+
<button class="inline-block rounded border border-gray-200 bg-transparent px-3 py-1.5 text-sm text-black"
|
|
60
|
+
onclick="setShowTrace(current => !current)">
|
|
61
|
+
{showTrace ? "Hide details" : "Show details"}
|
|
62
|
+
</button>
|
|
63
|
+
{% endif %}
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
{% if error_trace %}
|
|
69
|
+
<section class="mt-4" hidden="{!showTrace}">
|
|
70
|
+
<div class="mb-2 flex items-center justify-between">
|
|
71
|
+
<div class="text-xs text-gray-500">Error details (development only)</div>
|
|
72
|
+
<div class="flex gap-2">
|
|
73
|
+
<button class="rounded border bg-gray-100 px-2 py-1 text-xs text-black"
|
|
74
|
+
onclick="copyTrace()">
|
|
75
|
+
{traceCopied ? "Copied" : "Copy"}
|
|
76
|
+
</button>
|
|
77
|
+
<button class="rounded border bg-gray-100 px-2 py-1 text-xs text-black"
|
|
78
|
+
onclick="downloadTrace()">
|
|
79
|
+
Download
|
|
80
|
+
</button>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
<pre pp-ref="{tracePre}"
|
|
84
|
+
class="overflow-auto rounded bg-gray-100 p-4 text-xs text-black">{{ error_trace }}</pre>
|
|
85
|
+
</section>
|
|
86
|
+
{% endif %}
|
|
87
|
+
|
|
88
|
+
<script>
|
|
89
|
+
const [showTrace, setShowTrace] = pp.state(false);
|
|
90
|
+
const [traceCopied, setTraceCopied] = pp.state(false);
|
|
91
|
+
const tracePre = pp.ref(null);
|
|
92
|
+
|
|
93
|
+
async function copyTrace() {
|
|
94
|
+
if (!tracePre.current) return;
|
|
95
|
+
await navigator.clipboard.writeText(tracePre.current.innerText);
|
|
96
|
+
setTraceCopied(true);
|
|
97
|
+
setTimeout(() => setTraceCopied(false), 2000);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function downloadTrace() {
|
|
101
|
+
if (!tracePre.current) return;
|
|
102
|
+
const blob = new Blob([tracePre.current.innerText], { type: "text/plain" });
|
|
103
|
+
const url = URL.createObjectURL(blob);
|
|
104
|
+
const link = document.createElement("a");
|
|
105
|
+
link.href = url;
|
|
106
|
+
link.download = "error_trace.txt";
|
|
107
|
+
link.click();
|
|
108
|
+
URL.revokeObjectURL(url);
|
|
109
|
+
}
|
|
110
|
+
</script>
|
|
111
|
+
</div>
|
|
112
|
+
</main>
|
|
113
|
+
""", error_message=error_message, error_trace=error_trace)
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
from casp.component_decorator import html
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def page():
|
|
5
|
+
return html(r"""
|
|
1
6
|
<div
|
|
2
7
|
class="min-h-screen bg-background text-foreground selection:bg-primary/30 flex flex-col items-center justify-between py-8 md:py-12 px-6 md:px-24 overflow-x-hidden relative"
|
|
3
8
|
>
|
|
@@ -157,3 +162,4 @@
|
|
|
157
162
|
</a>
|
|
158
163
|
</div>
|
|
159
164
|
</div>
|
|
165
|
+
""")
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
def layout():
|
|
2
|
+
return r"""
|
|
3
|
+
<!DOCTYPE html>
|
|
4
|
+
<html lang="en">
|
|
5
|
+
<head>
|
|
6
|
+
<meta charset="UTF-8" />
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
|
+
<title>{{ metadata.title | default("Caspian App") }}</title>
|
|
9
|
+
<meta
|
|
10
|
+
name="description"
|
|
11
|
+
content="{{ metadata.description | default('Powered by Caspian Framework') }}"
|
|
12
|
+
/>
|
|
13
|
+
{% if metadata.robots %}
|
|
14
|
+
<meta name="robots" content="{{ metadata.robots }}" />
|
|
15
|
+
{% endif %}
|
|
16
|
+
<link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16" />
|
|
17
|
+
<link href="/css/styles.css" rel="stylesheet" />
|
|
18
|
+
<script type="module" src="/js/main.js"></script>
|
|
19
|
+
</head>
|
|
20
|
+
|
|
21
|
+
<body
|
|
22
|
+
style="
|
|
23
|
+
opacity: 0;
|
|
24
|
+
pointer-events: none;
|
|
25
|
+
user-select: none;
|
|
26
|
+
transition: opacity 0.18s ease-out;
|
|
27
|
+
"
|
|
28
|
+
>
|
|
29
|
+
<slot />
|
|
30
|
+
</body>
|
|
31
|
+
</html>
|
|
32
|
+
"""
|
|
@@ -1,18 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
</
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
from casp.component_decorator import html
|
|
2
|
+
from casp.layout import Metadata
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
metadata = Metadata(
|
|
6
|
+
title="Page Not Found",
|
|
7
|
+
description="The page you are looking for does not exist.",
|
|
8
|
+
extra={"robots": "noindex, nofollow"},
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def page():
|
|
13
|
+
return html(r"""
|
|
14
|
+
<main class="flex min-h-screen items-center justify-center bg-gray-50 px-4">
|
|
15
|
+
<div class="text-center">
|
|
16
|
+
<h1 class="text-9xl font-black text-gray-200">404</h1>
|
|
17
|
+
|
|
18
|
+
<p class="text-2xl font-bold tracking-tight text-gray-900 sm:text-4xl">
|
|
19
|
+
Uh-oh!
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
<p class="mt-4 text-gray-500">We can't find that page.</p>
|
|
23
|
+
|
|
24
|
+
<a
|
|
25
|
+
href="/"
|
|
26
|
+
class="mt-6 inline-block rounded bg-indigo-600 px-5 py-3 text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring"
|
|
27
|
+
>
|
|
28
|
+
Go Back Home
|
|
29
|
+
</a>
|
|
30
|
+
</div>
|
|
31
|
+
</main>
|
|
32
|
+
""")
|
package/dist/tests/README.md
CHANGED
|
@@ -105,7 +105,7 @@ reported `path:line:col`.
|
|
|
105
105
|
|
|
106
106
|
Removing "unused" imports is the one fix that is dangerous in this app. Caspian
|
|
107
107
|
single-file components import their children and then use them only as `<x-*>`
|
|
108
|
-
tags inside `html(...)
|
|
108
|
+
tags inside `html(...)` template strings (e.g. `from .Dialog
|
|
109
109
|
import DialogContent` → `<x-dialog-content>`). Ruff can't parse the template, so
|
|
110
110
|
it sees the import as unused — but casp resolves the tag from the module's
|
|
111
111
|
globals at render time, so deleting it breaks the page.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-caspian-app",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Scaffold a new Caspian project (FastAPI-powered reactive Python framework).",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"author": "Jefferson Abraham Omier <thesteelninjacode@gmail.com>",
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"chalk": "^
|
|
34
|
+
"chalk": "^6.0.0",
|
|
35
35
|
"prompts": "^2.4.2"
|
|
36
36
|
}
|
|
37
37
|
}
|
package/dist/src/app/error.html
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
<div class="container py-10">
|
|
2
|
-
<div class="max-w-2xl mx-auto bg-white shadow-sm rounded p-6">
|
|
3
|
-
<div class="flex items-start gap-4">
|
|
4
|
-
<svg class="w-10 h-10 text-red-600"
|
|
5
|
-
viewBox="0 0 24 24"
|
|
6
|
-
fill="none"
|
|
7
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
-
aria-hidden>
|
|
9
|
-
<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"
|
|
10
|
-
stroke="currentColor"
|
|
11
|
-
stroke-width="1.5"
|
|
12
|
-
stroke-linecap="round"
|
|
13
|
-
stroke-linejoin="round" />
|
|
14
|
-
<path d="M12 8v4"
|
|
15
|
-
stroke="currentColor"
|
|
16
|
-
stroke-width="1.5"
|
|
17
|
-
stroke-linecap="round"
|
|
18
|
-
stroke-linejoin="round" />
|
|
19
|
-
<path d="M12 16h.01"
|
|
20
|
-
stroke="currentColor"
|
|
21
|
-
stroke-width="1.5"
|
|
22
|
-
stroke-linecap="round"
|
|
23
|
-
stroke-linejoin="round" />
|
|
24
|
-
</svg>
|
|
25
|
-
|
|
26
|
-
<div class="flex-1">
|
|
27
|
-
<h1 class="text-2xl font-semibold text-red-600">
|
|
28
|
-
Something went wrong
|
|
29
|
-
</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
|
-
|
|
35
|
-
<p class="mt-3 text-sm text-gray-700">{{ error_message }}</p>
|
|
36
|
-
|
|
37
|
-
<div class="mt-4 flex flex-wrap gap-2">
|
|
38
|
-
<a href="/"
|
|
39
|
-
class="inline-block px-3 py-1.5 bg-gray-100 border border-gray-200 rounded text-sm text-gray-800 hover:bg-gray-200">Home</a>
|
|
40
|
-
<button class="inline-block px-3 py-1.5 bg-blue-600 text-white rounded text-sm"
|
|
41
|
-
onclick="reloadPage()">
|
|
42
|
-
Reload
|
|
43
|
-
</button>
|
|
44
|
-
{% if error_trace %}
|
|
45
|
-
<button id="toggleTraceBtn"
|
|
46
|
-
class="inline-block px-3 py-1.5 bg-transparent border border-gray-200 rounded text-sm text-black"
|
|
47
|
-
onclick="setShowTrace(showTrace => !showTrace)">
|
|
48
|
-
{showTrace ? "Hide details" : "Show details"}
|
|
49
|
-
</button>
|
|
50
|
-
{% endif %}
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
|
|
55
|
-
{% if error_trace %}
|
|
56
|
-
<div id="traceArea"
|
|
57
|
-
class="mt-4"
|
|
58
|
-
hidden="{!showTrace}">
|
|
59
|
-
<div class="flex items-center justify-between mb-2">
|
|
60
|
-
<div class="text-xs text-gray-500">Error details (soft copy)</div>
|
|
61
|
-
<div class="flex gap-2">
|
|
62
|
-
<button class="px-2 py-1 text-xs bg-gray-100 border rounded text-black"
|
|
63
|
-
onclick="copyTrace()">
|
|
64
|
-
{showCopyTrace ? "Copied" : "Copy"}
|
|
65
|
-
</button>
|
|
66
|
-
<button class="px-2 py-1 text-xs bg-gray-100 border rounded text-black"
|
|
67
|
-
onclick="downloadTrace()">
|
|
68
|
-
Download
|
|
69
|
-
</button>
|
|
70
|
-
</div>
|
|
71
|
-
</div>
|
|
72
|
-
<pre id="tracePre"
|
|
73
|
-
class="p-4 bg-gray-100 rounded overflow-auto text-xs text-black">{{ error_trace }}</pre>
|
|
74
|
-
</div>
|
|
75
|
-
{% endif %}
|
|
76
|
-
</div>
|
|
77
|
-
|
|
78
|
-
<script>
|
|
79
|
-
const [showTrace, setShowTrace] = pp.state(false);
|
|
80
|
-
const [showCopyTrace, setShowCopyTrace] = pp.state(false);
|
|
81
|
-
|
|
82
|
-
function reloadPage() {
|
|
83
|
-
window.location.reload();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function copyTrace() {
|
|
87
|
-
const tracePre = document.getElementById("tracePre");
|
|
88
|
-
navigator.clipboard.writeText(tracePre.innerText);
|
|
89
|
-
|
|
90
|
-
setShowCopyTrace(true);
|
|
91
|
-
setTimeout(() => {
|
|
92
|
-
setShowCopyTrace(false);
|
|
93
|
-
}, 2000);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function downloadTrace() {
|
|
97
|
-
const tracePre = document.getElementById("tracePre");
|
|
98
|
-
const blob = new Blob([tracePre.innerText], {
|
|
99
|
-
type: "text/plain",
|
|
100
|
-
});
|
|
101
|
-
const url = URL.createObjectURL(blob);
|
|
102
|
-
const a = document.createElement("a");
|
|
103
|
-
a.href = url;
|
|
104
|
-
a.download = "error_trace.txt";
|
|
105
|
-
document.body.appendChild(a);
|
|
106
|
-
a.click();
|
|
107
|
-
document.body.removeChild(a);
|
|
108
|
-
URL.revokeObjectURL(url);
|
|
109
|
-
}
|
|
110
|
-
</script>
|
|
111
|
-
</div>
|
package/dist/src/app/layout.html
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="UTF-8" />
|
|
6
|
-
<meta name="viewport"
|
|
7
|
-
content="width=device-width, initial-scale=1.0" />
|
|
8
|
-
<title>{{ metadata.title | default("Caspian App") }}</title>
|
|
9
|
-
<meta name="description"
|
|
10
|
-
content="{{ metadata.description | default('Powered by Caspian Framework') }}" />
|
|
11
|
-
<link rel="icon"
|
|
12
|
-
href="/favicon.ico"
|
|
13
|
-
type="image/x-icon"
|
|
14
|
-
sizes="16x16" />
|
|
15
|
-
</head>
|
|
16
|
-
|
|
17
|
-
<body style="
|
|
18
|
-
opacity: 0;
|
|
19
|
-
pointer-events: none;
|
|
20
|
-
user-select: none;
|
|
21
|
-
transition: opacity 0.18s ease-out;
|
|
22
|
-
">
|
|
23
|
-
<slot />
|
|
24
|
-
</body>
|
|
25
|
-
|
|
26
|
-
</html>
|