@lazycatcloud/lzc-cli 1.3.17 → 2.0.0-pre.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.
- package/README.md +47 -7
- package/changelog.md +14 -0
- package/lib/app/apkshell.js +7 -44
- package/lib/app/index.js +178 -64
- package/lib/app/lpk_build.js +446 -61
- package/lib/app/lpk_build_images.js +749 -0
- package/lib/app/lpk_create.js +192 -45
- package/lib/app/lpk_create_generator.js +141 -13
- package/lib/app/lpk_devshell.js +33 -19
- package/lib/app/lpk_embed_images.js +257 -0
- package/lib/app/lpk_installer.js +17 -9
- package/lib/app/manifest_build.js +259 -0
- package/lib/app/project_cp.js +59 -0
- package/lib/app/project_deploy.js +58 -0
- package/lib/app/project_exec.js +82 -0
- package/lib/app/project_info.js +106 -0
- package/lib/app/project_log.js +62 -0
- package/lib/app/project_runtime.js +356 -0
- package/lib/app/project_start.js +95 -0
- package/lib/app/project_sync.js +499 -0
- package/lib/appstore/apkshell.js +50 -0
- package/lib/box/index.js +101 -4
- package/lib/box/ssh_remote.js +259 -0
- package/lib/build_remote.js +21 -0
- package/lib/debug_bridge.js +891 -83
- package/lib/docker/index.js +30 -10
- package/lib/i18n/locales/en/translation.json +262 -255
- package/lib/i18n/locales/zh/translation.json +262 -255
- package/lib/lpk/core.js +488 -0
- package/lib/lpk/index.js +210 -0
- package/lib/migrate/index.js +52 -0
- package/lib/package_info.js +135 -0
- package/lib/shellapi.js +35 -1
- package/lib/sig/core.js +254 -0
- package/lib/sig/index.js +88 -0
- package/lib/utils.js +94 -15
- package/package.json +3 -3
- package/scripts/cli.js +6 -0
- package/scripts/smoke/frontend-dev-entry.mjs +104 -0
- package/scripts/smoke/template-project.mjs +311 -0
- package/template/_lpk/README.md +15 -4
- package/template/_lpk/gui-vnc.manifest.yml.in +18 -0
- package/template/_lpk/hello-vue.manifest.yml.in +38 -0
- package/template/_lpk/manifest.yml.in +4 -11
- package/template/_lpk/package.yml.in +7 -0
- package/template/_lpk/todolist-golang.manifest.yml.in +30 -0
- package/template/_lpk/todolist-java.manifest.yml.in +29 -0
- package/template/_lpk/todolist-python.manifest.yml.in +37 -0
- package/template/_lpk/todolist-serverless.manifest.yml.in +38 -0
- package/template/_lpk/vue.lzc-build.yml.in +0 -44
- package/template/blank/lzc-build.dev.yml +4 -0
- package/template/blank/lzc-build.yml +24 -41
- package/template/blank/lzc-manifest.yml +7 -9
- package/template/blank/package.yml +7 -0
- package/template/golang/Dockerfile +19 -0
- package/template/golang/Dockerfile.dev +20 -0
- package/template/golang/README.md +44 -0
- package/template/golang/_gitignore +3 -0
- package/template/golang/_lzcdevignore +21 -0
- package/template/golang/go.mod +3 -0
- package/template/golang/lzc-build.dev.yml +12 -0
- package/template/golang/lzc-build.yml +16 -0
- package/template/golang/lzc-icon.png +0 -0
- package/template/golang/main.go +252 -0
- package/template/golang/manifest.dev.page.js +24 -0
- package/template/golang/run.sh +10 -0
- package/template/golang/web/index.html +238 -0
- package/template/gui-vnc/README.md +23 -0
- package/template/gui-vnc/_gitignore +2 -0
- package/template/gui-vnc/images/Dockerfile +30 -0
- package/template/gui-vnc/images/kasmvnc.yaml +33 -0
- package/template/gui-vnc/images/startup-script.desktop +9 -0
- package/template/gui-vnc/images/startup-script.sh +6 -0
- package/template/gui-vnc/lzc-build.dev.yml +4 -0
- package/template/gui-vnc/lzc-build.yml +18 -0
- package/template/gui-vnc/lzc-icon.png +0 -0
- package/template/python/Dockerfile +15 -0
- package/template/python/Dockerfile.dev +18 -0
- package/template/python/README.md +50 -0
- package/template/python/_gitignore +3 -0
- package/template/python/_lzcdevignore +21 -0
- package/template/python/app.py +110 -0
- package/template/python/lzc-build.dev.yml +12 -0
- package/template/python/lzc-build.yml +16 -0
- package/template/python/lzc-icon.png +0 -0
- package/template/python/manifest.dev.page.js +25 -0
- package/template/python/requirements.txt +1 -0
- package/template/python/run.sh +14 -0
- package/template/python/web/index.html +238 -0
- package/template/springboot/Dockerfile +20 -0
- package/template/springboot/Dockerfile.dev +20 -0
- package/template/springboot/README.md +44 -0
- package/template/springboot/_gitignore +3 -0
- package/template/springboot/_lzcdevignore +21 -0
- package/template/springboot/lzc-build.dev.yml +12 -0
- package/template/springboot/lzc-build.yml +16 -0
- package/template/springboot/lzc-icon.png +0 -0
- package/template/springboot/manifest.dev.page.js +24 -0
- package/template/springboot/pom.xml +38 -0
- package/template/springboot/run.sh +10 -0
- package/template/springboot/src/main/java/cloud/lazycat/app/Application.java +132 -0
- package/template/springboot/src/main/resources/application.properties +1 -0
- package/template/springboot/src/main/resources/static/index.html +238 -0
- package/template/vue/README.md +18 -21
- package/template/vue/lzc-build.dev.yml +7 -0
- package/template/vue/lzc-build.yml +30 -43
- package/template/vue/manifest.dev.page.js +50 -0
- package/template/vue/src/App.vue +36 -25
- package/template/vue/src/style.css +106 -49
- package/template/vue-minidb/README.md +26 -0
- package/template/vue-minidb/_gitignore +25 -0
- package/template/vue-minidb/index.html +13 -0
- package/template/vue-minidb/lzc-build.dev.yml +7 -0
- package/template/vue-minidb/lzc-build.yml +46 -0
- package/template/vue-minidb/lzc-icon.png +0 -0
- package/template/vue-minidb/manifest.dev.page.js +50 -0
- package/template/vue-minidb/package.json +21 -0
- package/template/vue-minidb/public/vite.svg +1 -0
- package/template/vue-minidb/src/App.vue +206 -0
- package/template/vue-minidb/src/assets/vue.svg +1 -0
- package/template/vue-minidb/src/main.ts +5 -0
- package/template/vue-minidb/src/style.css +136 -0
- package/template/vue-minidb/src/vite-env.d.ts +1 -0
- package/template/vue-minidb/tsconfig.app.json +24 -0
- package/template/vue-minidb/tsconfig.json +7 -0
- package/template/vue-minidb/tsconfig.node.json +22 -0
- package/template/vue-minidb/vite.config.ts +10 -0
- /package/template/{vue → vue-minidb}/src/components/HelloWorld.vue +0 -0
package/template/vue/src/App.vue
CHANGED
|
@@ -1,30 +1,41 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
2
|
+
const highlights = [
|
|
3
|
+
{
|
|
4
|
+
title: 'Cross-platform by default',
|
|
5
|
+
detail: 'Build once and publish to desktop and mobile experiences in one flow.',
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
title: 'Fast local iteration',
|
|
9
|
+
detail: 'Use lzc-cli deploy/start loop to validate UI changes quickly.',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
title: 'Production packaging',
|
|
13
|
+
detail: 'Ship your app as an LPK package with predictable release behavior.',
|
|
14
|
+
},
|
|
15
|
+
]
|
|
3
16
|
</script>
|
|
4
17
|
|
|
5
18
|
<template>
|
|
6
|
-
<
|
|
7
|
-
<
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
<main class="hero-shell">
|
|
20
|
+
<section class="hero-card">
|
|
21
|
+
<p class="tag">Lazycat Cloud App Template</p>
|
|
22
|
+
<h1>Welcome to Lazycat Microservice</h1>
|
|
23
|
+
<p class="lead">
|
|
24
|
+
This Vue starter is tailored for Lazycat Cloud. Build your app UI, deploy with lzc-cli,
|
|
25
|
+
and deliver a consistent experience across devices.
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
<div class="cta-group">
|
|
29
|
+
<a class="btn primary" href="https://lazycat.cloud/" target="_blank" rel="noreferrer">Visit lazycat.cloud</a>
|
|
30
|
+
<a class="btn" href="https://developer.lazycat.cloud/" target="_blank" rel="noreferrer">Open Developer Docs</a>
|
|
31
|
+
</div>
|
|
16
32
|
|
|
17
|
-
<
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
.logo.vue:hover {
|
|
28
|
-
filter: drop-shadow(0 0 2em #42b883aa);
|
|
29
|
-
}
|
|
30
|
-
</style>
|
|
33
|
+
<ul class="highlight-grid">
|
|
34
|
+
<li v-for="item in highlights" :key="item.title" class="highlight-item">
|
|
35
|
+
<h2>{{ item.title }}</h2>
|
|
36
|
+
<p>{{ item.detail }}</p>
|
|
37
|
+
</li>
|
|
38
|
+
</ul>
|
|
39
|
+
</section>
|
|
40
|
+
</main>
|
|
41
|
+
</template>
|
|
@@ -1,79 +1,136 @@
|
|
|
1
1
|
:root {
|
|
2
|
-
|
|
2
|
+
--bg-1: #f8fbff;
|
|
3
|
+
--bg-2: #ecf6ff;
|
|
4
|
+
--card: #ffffff;
|
|
5
|
+
--text: #142033;
|
|
6
|
+
--muted: #4e617b;
|
|
7
|
+
--line: #d8e4f2;
|
|
8
|
+
--brand: #0076d6;
|
|
9
|
+
--brand-2: #00a5a5;
|
|
10
|
+
font-family: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
|
|
3
11
|
line-height: 1.5;
|
|
4
12
|
font-weight: 400;
|
|
5
|
-
|
|
6
|
-
color-scheme: light dark;
|
|
7
|
-
color: rgba(255, 255, 255, 0.87);
|
|
8
|
-
background-color: #242424;
|
|
9
|
-
|
|
10
|
-
font-synthesis: none;
|
|
13
|
+
color: var(--text);
|
|
11
14
|
text-rendering: optimizeLegibility;
|
|
12
15
|
-webkit-font-smoothing: antialiased;
|
|
13
16
|
-moz-osx-font-smoothing: grayscale;
|
|
14
17
|
}
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
color: #646cff;
|
|
19
|
-
text-decoration: inherit;
|
|
20
|
-
}
|
|
21
|
-
a:hover {
|
|
22
|
-
color: #535bf2;
|
|
19
|
+
* {
|
|
20
|
+
box-sizing: border-box;
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
body {
|
|
26
24
|
margin: 0;
|
|
27
|
-
display: flex;
|
|
28
|
-
place-items: center;
|
|
29
25
|
min-width: 320px;
|
|
26
|
+
background: radial-gradient(circle at top right, var(--bg-2), var(--bg-1) 42%);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#app {
|
|
30
30
|
min-height: 100vh;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
.hero-shell {
|
|
34
|
+
min-height: 100vh;
|
|
35
|
+
display: grid;
|
|
36
|
+
place-items: center;
|
|
37
|
+
padding: 28px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.hero-card {
|
|
41
|
+
width: min(960px, 100%);
|
|
42
|
+
background: var(--card);
|
|
43
|
+
border: 1px solid var(--line);
|
|
44
|
+
border-radius: 22px;
|
|
45
|
+
padding: 28px;
|
|
46
|
+
box-shadow: 0 18px 48px rgba(20, 32, 51, 0.08);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.tag {
|
|
50
|
+
margin: 0;
|
|
51
|
+
color: var(--brand);
|
|
52
|
+
font-size: 0.85rem;
|
|
53
|
+
font-weight: 700;
|
|
54
|
+
letter-spacing: 0.08em;
|
|
55
|
+
text-transform: uppercase;
|
|
56
|
+
}
|
|
57
|
+
|
|
33
58
|
h1 {
|
|
34
|
-
|
|
35
|
-
|
|
59
|
+
margin: 10px 0 12px;
|
|
60
|
+
font-size: clamp(1.8rem, 3.2vw, 2.7rem);
|
|
61
|
+
line-height: 1.15;
|
|
36
62
|
}
|
|
37
63
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
64
|
+
.lead {
|
|
65
|
+
margin: 0;
|
|
66
|
+
color: var(--muted);
|
|
67
|
+
max-width: 70ch;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.cta-group {
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-wrap: wrap;
|
|
73
|
+
gap: 10px;
|
|
74
|
+
margin-top: 18px;
|
|
48
75
|
}
|
|
49
|
-
|
|
50
|
-
|
|
76
|
+
|
|
77
|
+
.btn {
|
|
78
|
+
display: inline-flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
justify-content: center;
|
|
81
|
+
text-decoration: none;
|
|
82
|
+
padding: 10px 14px;
|
|
83
|
+
border-radius: 10px;
|
|
84
|
+
border: 1px solid var(--line);
|
|
85
|
+
color: var(--text);
|
|
86
|
+
font-weight: 600;
|
|
87
|
+
transition: transform 120ms ease, box-shadow 120ms ease;
|
|
51
88
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
89
|
+
|
|
90
|
+
.btn:hover {
|
|
91
|
+
transform: translateY(-1px);
|
|
92
|
+
box-shadow: 0 8px 20px rgba(20, 32, 51, 0.08);
|
|
55
93
|
}
|
|
56
94
|
|
|
57
|
-
.
|
|
58
|
-
|
|
95
|
+
.btn.primary {
|
|
96
|
+
color: #fff;
|
|
97
|
+
border-color: transparent;
|
|
98
|
+
background: linear-gradient(135deg, var(--brand), var(--brand-2));
|
|
59
99
|
}
|
|
60
100
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
margin: 0
|
|
64
|
-
padding:
|
|
65
|
-
|
|
101
|
+
.highlight-grid {
|
|
102
|
+
list-style: none;
|
|
103
|
+
margin: 22px 0 0;
|
|
104
|
+
padding: 0;
|
|
105
|
+
display: grid;
|
|
106
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
107
|
+
gap: 12px;
|
|
66
108
|
}
|
|
67
109
|
|
|
68
|
-
|
|
69
|
-
:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
110
|
+
.highlight-item {
|
|
111
|
+
border: 1px solid var(--line);
|
|
112
|
+
border-radius: 14px;
|
|
113
|
+
padding: 14px;
|
|
114
|
+
background: #fbfdff;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.highlight-item h2 {
|
|
118
|
+
margin: 0;
|
|
119
|
+
font-size: 1rem;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.highlight-item p {
|
|
123
|
+
margin: 8px 0 0;
|
|
124
|
+
color: var(--muted);
|
|
125
|
+
font-size: 0.93rem;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@media (max-width: 900px) {
|
|
129
|
+
.highlight-grid {
|
|
130
|
+
grid-template-columns: 1fr;
|
|
75
131
|
}
|
|
76
|
-
|
|
77
|
-
|
|
132
|
+
|
|
133
|
+
.hero-card {
|
|
134
|
+
padding: 20px;
|
|
78
135
|
}
|
|
79
136
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Lazycat Serverless Todo App
|
|
2
|
+
|
|
3
|
+
## First Deploy
|
|
4
|
+
```bash
|
|
5
|
+
lzc-cli project deploy
|
|
6
|
+
lzc-cli project info
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
By default, project commands use `lzc-build.dev.yml` when it exists.
|
|
10
|
+
Each command prints the active `Build config`.
|
|
11
|
+
Use `--release` if you want to operate on `lzc-build.yml`.
|
|
12
|
+
|
|
13
|
+
Open the app first.
|
|
14
|
+
If the frontend dev server is not running yet, the app page will show the expected port and next step.
|
|
15
|
+
|
|
16
|
+
## Frontend Dev Loop
|
|
17
|
+
```bash
|
|
18
|
+
npm run dev
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then refresh the app page.
|
|
22
|
+
|
|
23
|
+
## Logs
|
|
24
|
+
```bash
|
|
25
|
+
lzc-cli project log -f
|
|
26
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
node_modules
|
|
3
|
+
dist
|
|
4
|
+
*.lpk
|
|
5
|
+
|
|
6
|
+
# local env files
|
|
7
|
+
.env.local
|
|
8
|
+
.env.*.local
|
|
9
|
+
|
|
10
|
+
# Log files
|
|
11
|
+
npm-debug.log*
|
|
12
|
+
yarn-debug.log*
|
|
13
|
+
yarn-error.log*
|
|
14
|
+
pnpm-debug.log*
|
|
15
|
+
|
|
16
|
+
# Editor directories and files
|
|
17
|
+
.idea
|
|
18
|
+
.vscode
|
|
19
|
+
*.suo
|
|
20
|
+
*.ntvs*
|
|
21
|
+
*.njsproj
|
|
22
|
+
*.sln
|
|
23
|
+
*.sw?
|
|
24
|
+
.temp
|
|
25
|
+
.cache
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Vite + Vue + TS</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.ts"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# buildscript
|
|
2
|
+
# - 可以为构建脚本的路径地址
|
|
3
|
+
# - 如果构建命令简单,也可以直接写 sh 的命令
|
|
4
|
+
buildscript: npm install && npm run build
|
|
5
|
+
|
|
6
|
+
# manifest: 指定 lpk 包的 manifest.yml 文件路径
|
|
7
|
+
manifest: ./lzc-manifest.yml
|
|
8
|
+
|
|
9
|
+
# contentdir: 指定打包的内容,将会打包到 lpk 中
|
|
10
|
+
contentdir: ./dist
|
|
11
|
+
|
|
12
|
+
# images: 在盒子侧构建镜像并写入 lpk v2 images 目录
|
|
13
|
+
# - key 是 image alias
|
|
14
|
+
# - 在 lzc-manifest.yml 中通过 embed:alias 引用
|
|
15
|
+
# - dockerfile 与 dockerfile-content 二选一
|
|
16
|
+
# - context 可选,默认是 dockerfile 所在目录
|
|
17
|
+
# images: 在盒子侧构建镜像并写入 lpk v2 images 目录
|
|
18
|
+
# - key 是 image alias
|
|
19
|
+
# - 在 lzc-manifest.yml 中通过 embed:alias 引用
|
|
20
|
+
# - dockerfile 与 dockerfile-content 二选一
|
|
21
|
+
# - context 可选,默认是 dockerfile 所在目录
|
|
22
|
+
# images:
|
|
23
|
+
# my-nginx:
|
|
24
|
+
# context: ./image
|
|
25
|
+
# dockerfile: ./image/Dockerfile
|
|
26
|
+
# # 可选,默认 registry.lazycat.cloud
|
|
27
|
+
# upstream-match: registry.lazycat.cloud
|
|
28
|
+
# other-image:
|
|
29
|
+
# dockerfile: ./other/Dockerfile
|
|
30
|
+
# inline-image:
|
|
31
|
+
# context: ./
|
|
32
|
+
# dockerfile-content: |
|
|
33
|
+
# FROM alpine:3.20
|
|
34
|
+
# RUN echo "hello"
|
|
35
|
+
#
|
|
36
|
+
# 构建时默认是混合模式:
|
|
37
|
+
# - 先沿父镜像链向上找 upstream
|
|
38
|
+
# - 找到 upstream 后,仅 embed 非 upstream 的 layers
|
|
39
|
+
# - 如果未匹配到 upstream,则自动全量 embed
|
|
40
|
+
|
|
41
|
+
# pkgout: lpk 包的输出路径
|
|
42
|
+
pkgout: ./
|
|
43
|
+
|
|
44
|
+
# icon 指定 lpk 包 icon 的路径路径,如果不指定将会警告
|
|
45
|
+
# icon 仅仅允许 png 后缀的文件
|
|
46
|
+
icon: ./lzc-icon.png
|
|
Binary file
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
function renderDevPage(state, port) {
|
|
2
|
+
const pageTitle = "Frontend Dev";
|
|
3
|
+
const portText = port === undefined || port === null ? "" : String(port);
|
|
4
|
+
const configs = {
|
|
5
|
+
not_linked: {
|
|
6
|
+
title: "Dev machine is not linked yet",
|
|
7
|
+
subtitle: "This app is waiting for a frontend dev server from the machine that runs lzc-cli project deploy.",
|
|
8
|
+
steps: [
|
|
9
|
+
"Run <code>lzc-cli project deploy</code> on your dev machine.",
|
|
10
|
+
"Start your local dev server with <code>npm run dev</code>.",
|
|
11
|
+
"Refresh this page after the dev server is listening on port <code>" + portText + "</code>.",
|
|
12
|
+
],
|
|
13
|
+
},
|
|
14
|
+
offline: {
|
|
15
|
+
title: "Dev machine is offline",
|
|
16
|
+
subtitle: "The linked dev machine is not reachable right now.",
|
|
17
|
+
steps: [
|
|
18
|
+
"Bring the dev machine online.",
|
|
19
|
+
"Start your local dev server with <code>npm run dev</code>.",
|
|
20
|
+
"Refresh this page after port <code>" + portText + "</code> is ready.",
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
not_ready: {
|
|
24
|
+
title: "Frontend dev server is not ready",
|
|
25
|
+
subtitle: "This app is running in dev mode and will proxy requests to your linked dev machine.",
|
|
26
|
+
steps: [
|
|
27
|
+
"Start your local dev server with <code>npm run dev</code>.",
|
|
28
|
+
"Make sure the dev server listens on port <code>" + portText + "</code>.",
|
|
29
|
+
"Refresh this page after the server is ready.",
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
const config = configs[state] ?? configs.not_ready;
|
|
34
|
+
const title = config.title;
|
|
35
|
+
const subtitle = config.subtitle;
|
|
36
|
+
const steps = config.steps;
|
|
37
|
+
const items = steps.map(function (step) {
|
|
38
|
+
return "<li>" + step + "</li>";
|
|
39
|
+
}).join("");
|
|
40
|
+
const portBadge = portText ? "<div class=\"pill\">Expected local port: " + portText + "</div>" : "";
|
|
41
|
+
return [
|
|
42
|
+
"<!doctype html>",
|
|
43
|
+
"<html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>", pageTitle, "</title><style>",
|
|
44
|
+
"body{margin:0;padding:32px;font-family:ui-sans-serif,system-ui,sans-serif;background:#f5f8fc;color:#112033}",
|
|
45
|
+
".card{max-width:760px;margin:0 auto;background:#fff;border:1px solid #d9e4f2;border-radius:18px;padding:24px;box-shadow:0 16px 40px rgba(17,32,51,.08)}",
|
|
46
|
+
"h1{margin:0 0 12px;font-size:32px}.muted{color:#51627a}.pill{display:inline-block;margin:16px 0 0;padding:6px 10px;border-radius:999px;background:#eef5ff;color:#0c5dd6;font-weight:600}",
|
|
47
|
+
"ol{margin:18px 0 0;padding-left:20px;line-height:1.8}code{background:#f2f6fb;border-radius:8px;padding:2px 6px}button{margin-top:20px;border:0;border-radius:10px;padding:10px 14px;background:#112033;color:#fff;cursor:pointer}",
|
|
48
|
+
"</style></head><body><main class=\"card\"><h1>", title, "</h1><p class=\"muted\">", subtitle, "</p>", portBadge, "<ol>", items, "</ol><button onclick=\"location.reload()\">Refresh</button></main></body></html>",
|
|
49
|
+
].join("");
|
|
50
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "lzc-app-template",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite --port 3000 --host",
|
|
8
|
+
"build": "vue-tsc -b && vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@lazycatcloud/minidb": "^0.0.10",
|
|
13
|
+
"vue": "^3.4.37"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@vitejs/plugin-vue": "^5.1.2",
|
|
17
|
+
"typescript": "^5.5.3",
|
|
18
|
+
"vite": "^5.4.1",
|
|
19
|
+
"vue-tsc": "^2.0.29"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { MiniDB } from '@lazycatcloud/minidb'
|
|
3
|
+
import { onMounted, onUnmounted, ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
type Todo = {
|
|
6
|
+
_id?: string
|
|
7
|
+
title: string
|
|
8
|
+
done: boolean
|
|
9
|
+
updatedAt: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const db = new MiniDB()
|
|
13
|
+
const todosCollection = db.getCollection('todos')
|
|
14
|
+
|
|
15
|
+
const todos = ref<Todo[]>([])
|
|
16
|
+
const inputValue = ref('')
|
|
17
|
+
let syncTimer: ReturnType<typeof setInterval> | undefined
|
|
18
|
+
|
|
19
|
+
async function loadTodos() {
|
|
20
|
+
const next = await todosCollection.find({}, { sort: [['updatedAt', 'desc']] }).fetch()
|
|
21
|
+
const prevSerialized = JSON.stringify(todos.value)
|
|
22
|
+
const nextSerialized = JSON.stringify(next)
|
|
23
|
+
if (prevSerialized !== nextSerialized) {
|
|
24
|
+
todos.value = next
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function syncTodos() {
|
|
29
|
+
await loadTodos()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function addTodo() {
|
|
33
|
+
const title = inputValue.value.trim()
|
|
34
|
+
if (!title) {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await todosCollection.upsert({
|
|
39
|
+
title,
|
|
40
|
+
done: false,
|
|
41
|
+
updatedAt: Date.now(),
|
|
42
|
+
})
|
|
43
|
+
inputValue.value = ''
|
|
44
|
+
await loadTodos()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function toggleTodo(todo: Todo) {
|
|
48
|
+
await todosCollection.upsert(
|
|
49
|
+
{
|
|
50
|
+
...todo,
|
|
51
|
+
done: !todo.done,
|
|
52
|
+
updatedAt: Date.now(),
|
|
53
|
+
},
|
|
54
|
+
todo,
|
|
55
|
+
)
|
|
56
|
+
await loadTodos()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function removeTodo(todo: Todo) {
|
|
60
|
+
if (!todo._id) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
await todosCollection.remove(todo._id)
|
|
64
|
+
await loadTodos()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
onMounted(async () => {
|
|
68
|
+
await syncTodos()
|
|
69
|
+
syncTimer = setInterval(syncTodos, 2500)
|
|
70
|
+
document.addEventListener('visibilitychange', syncTodos)
|
|
71
|
+
window.addEventListener('focus', syncTodos)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
onUnmounted(() => {
|
|
75
|
+
if (syncTimer) {
|
|
76
|
+
clearInterval(syncTimer)
|
|
77
|
+
}
|
|
78
|
+
document.removeEventListener('visibilitychange', syncTodos)
|
|
79
|
+
window.removeEventListener('focus', syncTodos)
|
|
80
|
+
})
|
|
81
|
+
</script>
|
|
82
|
+
|
|
83
|
+
<template>
|
|
84
|
+
<main class="hero-shell">
|
|
85
|
+
<section class="hero-card">
|
|
86
|
+
<p class="tag">Serverless Version</p>
|
|
87
|
+
<h1>Todo demo powered by Vue + MiniDB</h1>
|
|
88
|
+
<p class="lead">
|
|
89
|
+
This template runs without a custom backend service and stores data with
|
|
90
|
+
@lazycatcloud/minidb.
|
|
91
|
+
</p>
|
|
92
|
+
|
|
93
|
+
<div class="cta-group">
|
|
94
|
+
<a class="btn primary" href="https://lazycat.cloud/" target="_blank" rel="noreferrer">Visit lazycat.cloud</a>
|
|
95
|
+
<a class="btn" href="https://developer.lazycat.cloud/" target="_blank" rel="noreferrer">Open Developer Docs</a>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<section class="todo-panel">
|
|
99
|
+
<h2>MiniDB quick demo</h2>
|
|
100
|
+
<p class="sync-note">Data syncs automatically across devices when opened with the same app account.</p>
|
|
101
|
+
<div class="form">
|
|
102
|
+
<input v-model="inputValue" placeholder="Add a task for your app kickoff" @keyup.enter="addTodo" />
|
|
103
|
+
<button @click="addTodo">Add</button>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<ul class="list">
|
|
107
|
+
<li v-for="todo in todos" :key="todo._id ?? `${todo.title}-${todo.updatedAt}`">
|
|
108
|
+
<label>
|
|
109
|
+
<input type="checkbox" :checked="todo.done" @change="toggleTodo(todo)" />
|
|
110
|
+
<span :class="{ done: todo.done }">{{ todo.title }}</span>
|
|
111
|
+
</label>
|
|
112
|
+
<button class="danger" @click="removeTodo(todo)">Delete</button>
|
|
113
|
+
</li>
|
|
114
|
+
</ul>
|
|
115
|
+
</section>
|
|
116
|
+
</section>
|
|
117
|
+
</main>
|
|
118
|
+
</template>
|
|
119
|
+
|
|
120
|
+
<style scoped>
|
|
121
|
+
.todo-panel {
|
|
122
|
+
margin-top: 22px;
|
|
123
|
+
border: 1px solid var(--line);
|
|
124
|
+
border-radius: 14px;
|
|
125
|
+
padding: 14px;
|
|
126
|
+
background: #fbfdff;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.todo-panel h2 {
|
|
130
|
+
margin: 0;
|
|
131
|
+
font-size: 1.05rem;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.sync-note {
|
|
135
|
+
margin: 8px 0 0;
|
|
136
|
+
color: var(--muted);
|
|
137
|
+
font-size: 0.9rem;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.form {
|
|
141
|
+
display: flex;
|
|
142
|
+
gap: 10px;
|
|
143
|
+
margin: 12px 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
input {
|
|
147
|
+
flex: 1;
|
|
148
|
+
border: 1px solid var(--line);
|
|
149
|
+
border-radius: 10px;
|
|
150
|
+
padding: 10px 12px;
|
|
151
|
+
min-width: 0;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.list {
|
|
155
|
+
list-style: none;
|
|
156
|
+
margin: 0;
|
|
157
|
+
padding: 0;
|
|
158
|
+
display: grid;
|
|
159
|
+
gap: 10px;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.list li {
|
|
163
|
+
display: flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
justify-content: space-between;
|
|
166
|
+
gap: 8px;
|
|
167
|
+
border: 1px solid var(--line);
|
|
168
|
+
border-radius: 10px;
|
|
169
|
+
padding: 10px 12px;
|
|
170
|
+
background: #fff;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
label {
|
|
174
|
+
display: flex;
|
|
175
|
+
align-items: center;
|
|
176
|
+
gap: 8px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.done {
|
|
180
|
+
text-decoration: line-through;
|
|
181
|
+
color: var(--muted);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
button {
|
|
185
|
+
border: 1px solid var(--line);
|
|
186
|
+
border-radius: 10px;
|
|
187
|
+
padding: 9px 12px;
|
|
188
|
+
cursor: pointer;
|
|
189
|
+
background: #fff;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.danger {
|
|
193
|
+
color: #a63e37;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@media (max-width: 640px) {
|
|
197
|
+
.form {
|
|
198
|
+
flex-direction: column;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.list li {
|
|
202
|
+
align-items: flex-start;
|
|
203
|
+
flex-direction: column;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
</style>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|