@alstar/studio 0.0.0-beta.10 → 0.0.0-beta.12
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/api/api-key.ts +37 -49
- package/api/auth.ts +64 -0
- package/api/backup.ts +39 -28
- package/api/block.ts +80 -119
- package/api/index.ts +17 -10
- package/api/mcp.ts +39 -42
- package/components/AdminPanel.ts +29 -4
- package/components/BlockFieldRenderer.ts +20 -4
- package/components/Entries.ts +7 -3
- package/components/FieldRenderer.ts +1 -1
- package/components/Settings.ts +5 -93
- package/components/SiteLayout.ts +11 -9
- package/components/fields/Markdown.ts +1 -1
- package/components/fields/Slug.ts +122 -0
- package/components/fields/Text.ts +20 -4
- package/components/fields/index.ts +4 -1
- package/components/icons.ts +55 -0
- package/components/settings/ApiKeys.ts +122 -0
- package/components/settings/Backup.ts +82 -0
- package/components/settings/Users.ts +63 -0
- package/index.ts +51 -11
- package/package.json +6 -3
- package/pages/entry/[id].ts +1 -3
- package/pages/error.ts +14 -0
- package/pages/index.ts +1 -1
- package/pages/login.ts +21 -0
- package/pages/register.ts +33 -0
- package/pages/settings.ts +1 -3
- package/public/studio/css/settings.css +7 -15
- package/public/studio/js/markdown-editor.js +1 -1
- package/public/studio/js/sortable-list.js +1 -1
- package/public/studio/main.css +5 -1
- package/public/studio/main.js +13 -0
- package/queries/block-2.ts +339 -0
- package/queries/getBlockTrees-2.ts +71 -0
- package/queries/index.ts +1 -1
- package/readme.md +2 -2
- package/schema.sql +18 -0
- package/schemas.ts +11 -1
- package/types.ts +11 -0
- package/utils/auth.ts +54 -0
- package/utils/create-hash.ts +9 -0
- package/utils/define.ts +1 -3
- package/utils/renderSSE.ts +27 -0
- package/utils/slugify.ts +3 -1
- package/utils/startup-log.ts +15 -6
- package/components/Backup.ts +0 -10
- package/utils/build-structure-path.ts +0 -43
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { html } from 'hono/html'
|
|
2
|
+
import * as icons from '../icons.ts'
|
|
3
|
+
import { db } from '@alstar/db'
|
|
4
|
+
import { sql } from '../../utils/sql.ts'
|
|
5
|
+
|
|
6
|
+
export default () => {
|
|
7
|
+
const apiKeys = db.database
|
|
8
|
+
.prepare(sql`
|
|
9
|
+
select
|
|
10
|
+
*
|
|
11
|
+
from
|
|
12
|
+
api_keys
|
|
13
|
+
`)
|
|
14
|
+
.all()
|
|
15
|
+
|
|
16
|
+
return html`
|
|
17
|
+
<article data-signals="{ apiKey: '', copied: false }">
|
|
18
|
+
<header>API Keys</header>
|
|
19
|
+
|
|
20
|
+
<table class="striped">
|
|
21
|
+
<thead>
|
|
22
|
+
<tr>
|
|
23
|
+
<th scope="col">Name</th>
|
|
24
|
+
<th scope="col">Value</th>
|
|
25
|
+
<th scope="col">Delete</th>
|
|
26
|
+
</tr>
|
|
27
|
+
</thead>
|
|
28
|
+
<tbody>
|
|
29
|
+
${apiKeys.map((apiKey) => {
|
|
30
|
+
return html`
|
|
31
|
+
<tr>
|
|
32
|
+
<th scope="row">${apiKey.name}</th>
|
|
33
|
+
<td><input type="text" disabled value="${apiKey.hint}" /></td>
|
|
34
|
+
<td>
|
|
35
|
+
<form
|
|
36
|
+
data-on-submit="@delete('/studio/api/api-key', {
|
|
37
|
+
contentType: 'form',
|
|
38
|
+
headers: {
|
|
39
|
+
render: 'Settings'
|
|
40
|
+
}
|
|
41
|
+
})"
|
|
42
|
+
>
|
|
43
|
+
<button
|
|
44
|
+
data-tooltip="Delete API key"
|
|
45
|
+
data-placement="left"
|
|
46
|
+
type="submit"
|
|
47
|
+
class="ghost"
|
|
48
|
+
>
|
|
49
|
+
${icons.trash}
|
|
50
|
+
</button>
|
|
51
|
+
|
|
52
|
+
<input type="hidden" name="value" value="${apiKey.value}" />
|
|
53
|
+
</form>
|
|
54
|
+
</td>
|
|
55
|
+
</tr>`
|
|
56
|
+
})}
|
|
57
|
+
</tbody>
|
|
58
|
+
</table>
|
|
59
|
+
|
|
60
|
+
<form
|
|
61
|
+
data-on-submit="@post('/studio/api/api-key', {
|
|
62
|
+
contentType: 'form',
|
|
63
|
+
headers: {
|
|
64
|
+
render: 'Settings'
|
|
65
|
+
}
|
|
66
|
+
})"
|
|
67
|
+
>
|
|
68
|
+
<label for="api_key_name"><small>Generate API Key</small></label>
|
|
69
|
+
|
|
70
|
+
<input
|
|
71
|
+
data-bind="name"
|
|
72
|
+
type="text"
|
|
73
|
+
name="name"
|
|
74
|
+
id="api_key_name"
|
|
75
|
+
placeholder="Name"
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
<button type="submit" class="ghost">Generate key</button>
|
|
79
|
+
</form>
|
|
80
|
+
|
|
81
|
+
<dialog data-attr="{ open: $apiKey !== '' }">
|
|
82
|
+
<article>
|
|
83
|
+
<header>
|
|
84
|
+
<p>API Key</p>
|
|
85
|
+
</header>
|
|
86
|
+
<p>Be sure to save this key, as it wont be shown again.</p>
|
|
87
|
+
|
|
88
|
+
<div style="display: flex; gap: 1rem; align-items: center;">
|
|
89
|
+
<h3 style="margin: 0;">
|
|
90
|
+
<code data-text="$apiKey"></code>
|
|
91
|
+
</h3>
|
|
92
|
+
|
|
93
|
+
<button
|
|
94
|
+
style="display: flex; align-items: center;"
|
|
95
|
+
data-attr="{ id: $apiKey }"
|
|
96
|
+
data-on-click="navigator.clipboard.writeText($apiKey); $copied = true"
|
|
97
|
+
class="ghost"
|
|
98
|
+
aria-label="Copy key to clipboard"
|
|
99
|
+
>
|
|
100
|
+
${icons.clipboard}
|
|
101
|
+
<span
|
|
102
|
+
style="display: none; margin-left: 0.5rem; color: green;"
|
|
103
|
+
data-style="{ display: $copied && 'block' }"
|
|
104
|
+
>
|
|
105
|
+
Copied
|
|
106
|
+
</span>
|
|
107
|
+
</button>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<footer>
|
|
111
|
+
<button
|
|
112
|
+
class="ghost"
|
|
113
|
+
data-on-click="$apiKey = ''; $copied = false; evt.target.closest('dialog')?.close()"
|
|
114
|
+
>
|
|
115
|
+
Close
|
|
116
|
+
</button>
|
|
117
|
+
</footer>
|
|
118
|
+
</article>
|
|
119
|
+
</dialog>
|
|
120
|
+
</article>
|
|
121
|
+
`
|
|
122
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fsp from 'node:fs/promises'
|
|
2
|
+
import { html } from 'hono/html'
|
|
3
|
+
import * as icons from '../icons.ts'
|
|
4
|
+
|
|
5
|
+
export default async () => {
|
|
6
|
+
const backupDir = './backups'
|
|
7
|
+
const backups = await fsp.readdir(backupDir)
|
|
8
|
+
|
|
9
|
+
return html`
|
|
10
|
+
<article data-signals="{ status: null, message: '' }">
|
|
11
|
+
<header>Backup</header>
|
|
12
|
+
|
|
13
|
+
<table class="striped">
|
|
14
|
+
<thead>
|
|
15
|
+
<tr>
|
|
16
|
+
<th scope="col">File</th>
|
|
17
|
+
<th scope="col">Download</th>
|
|
18
|
+
<th scope="col">Delete</th>
|
|
19
|
+
</tr>
|
|
20
|
+
</thead>
|
|
21
|
+
<tbody>
|
|
22
|
+
${backups.map(
|
|
23
|
+
(filename) => html`
|
|
24
|
+
<tr>
|
|
25
|
+
<th scope="row">${filename}</th>
|
|
26
|
+
<th>
|
|
27
|
+
<a
|
|
28
|
+
href="/studio/backups/${filename}"
|
|
29
|
+
role="button"
|
|
30
|
+
target="_blank"
|
|
31
|
+
download
|
|
32
|
+
class="ghost square"
|
|
33
|
+
aria-label="Download backup"
|
|
34
|
+
>
|
|
35
|
+
${icons.download}
|
|
36
|
+
</a>
|
|
37
|
+
</th>
|
|
38
|
+
<th>
|
|
39
|
+
<form
|
|
40
|
+
data-on-submit="@delete('/studio/api/backup', {
|
|
41
|
+
contentType: 'form',
|
|
42
|
+
headers: {
|
|
43
|
+
render: 'Settings'
|
|
44
|
+
}
|
|
45
|
+
})"
|
|
46
|
+
>
|
|
47
|
+
<input type="hidden" name="filename" value="${filename}" />
|
|
48
|
+
<button class="ghost square">${icons.trash}</button>
|
|
49
|
+
</form>
|
|
50
|
+
</th>
|
|
51
|
+
</tr>
|
|
52
|
+
`,
|
|
53
|
+
)}
|
|
54
|
+
</tbody>
|
|
55
|
+
</table>
|
|
56
|
+
|
|
57
|
+
<form
|
|
58
|
+
data-on-submit="@post('/studio/api/backup', {
|
|
59
|
+
contentType: 'form',
|
|
60
|
+
headers: {
|
|
61
|
+
render: 'Settings'
|
|
62
|
+
}
|
|
63
|
+
})"
|
|
64
|
+
>
|
|
65
|
+
<button type="submit">Backup database</button>
|
|
66
|
+
</form>
|
|
67
|
+
|
|
68
|
+
<hr>
|
|
69
|
+
|
|
70
|
+
<form
|
|
71
|
+
data-on-submit="@post('/studio/api/backup', { contentType: 'form' })"
|
|
72
|
+
>
|
|
73
|
+
<input type="file" name="file" />
|
|
74
|
+
<button type="submit" class="ghost">Restore database</button>
|
|
75
|
+
<!-- <p
|
|
76
|
+
data-style-color="$status === 200 ? 'green' : 'red'"
|
|
77
|
+
data-text="$message || ' '"
|
|
78
|
+
></p> -->
|
|
79
|
+
</form>
|
|
80
|
+
</article>
|
|
81
|
+
`
|
|
82
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { db } from '@alstar/db'
|
|
2
|
+
import { html } from 'hono/html'
|
|
3
|
+
import { sql } from '../../utils/sql.ts'
|
|
4
|
+
|
|
5
|
+
export default () => {
|
|
6
|
+
const users = db.database
|
|
7
|
+
.prepare(sql`
|
|
8
|
+
select
|
|
9
|
+
email
|
|
10
|
+
from
|
|
11
|
+
users
|
|
12
|
+
`)
|
|
13
|
+
.all()
|
|
14
|
+
|
|
15
|
+
return html`
|
|
16
|
+
<article>
|
|
17
|
+
<header>Users</header>
|
|
18
|
+
|
|
19
|
+
<table class="striped">
|
|
20
|
+
<thead>
|
|
21
|
+
<tr>
|
|
22
|
+
<th scope="col">Email</th>
|
|
23
|
+
</tr>
|
|
24
|
+
</thead>
|
|
25
|
+
<tbody>
|
|
26
|
+
${users.map(
|
|
27
|
+
(user) => html`
|
|
28
|
+
<tr>
|
|
29
|
+
<th scope="row">${user.email}</th>
|
|
30
|
+
</tr>
|
|
31
|
+
`,
|
|
32
|
+
)}
|
|
33
|
+
</tbody>
|
|
34
|
+
</table>
|
|
35
|
+
|
|
36
|
+
<hr>
|
|
37
|
+
|
|
38
|
+
<article>
|
|
39
|
+
<header>Register user</header>
|
|
40
|
+
<form
|
|
41
|
+
data-on-submit="@post('/studio/api/auth/register', { contentType: 'form' })"
|
|
42
|
+
>
|
|
43
|
+
<label for="register_email"><small>Email</small></label>
|
|
44
|
+
<input
|
|
45
|
+
id="register_email"
|
|
46
|
+
name="email"
|
|
47
|
+
type="email"
|
|
48
|
+
placeholder="Email"
|
|
49
|
+
/>
|
|
50
|
+
<label for="register_password"><small>Password</small></label>
|
|
51
|
+
<input
|
|
52
|
+
id="register_password"
|
|
53
|
+
name="password"
|
|
54
|
+
type="password"
|
|
55
|
+
placeholder="Password"
|
|
56
|
+
/>
|
|
57
|
+
<br />
|
|
58
|
+
<button type="submit" class="ghost">Create</button>
|
|
59
|
+
</form>
|
|
60
|
+
</article>
|
|
61
|
+
</article>
|
|
62
|
+
`
|
|
63
|
+
}
|
package/index.ts
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
|
|
1
3
|
import { Hono } from 'hono'
|
|
2
|
-
import { loadDb } from '@alstar/db'
|
|
3
4
|
import { serve } from '@hono/node-server'
|
|
4
5
|
import { serveStatic } from '@hono/node-server/serve-static'
|
|
6
|
+
import { HTTPException } from 'hono/http-exception'
|
|
7
|
+
|
|
8
|
+
import { loadDb } from '@alstar/db'
|
|
5
9
|
import { createRefresher } from '@alstar/refresher'
|
|
6
10
|
|
|
7
|
-
import * as types from './types.ts'
|
|
8
11
|
import { createStudioTables } from './utils/create-studio-tables.ts'
|
|
9
12
|
import { fileBasedRouter } from './utils/file-based-router.ts'
|
|
10
13
|
import { getConfig } from './utils/get-config.ts'
|
|
11
14
|
import startupLog from './utils/startup-log.ts'
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
|
|
15
|
+
import { apiRoutes } from './api/index.ts'
|
|
16
|
+
import { mcpRoutes } from './api/mcp.ts'
|
|
17
|
+
|
|
18
|
+
import auth from './utils/auth.ts'
|
|
19
|
+
import ErrorPage from './pages/error.ts'
|
|
20
|
+
|
|
21
|
+
import * as types from './types.ts'
|
|
15
22
|
|
|
16
23
|
export let rootdir = './node_modules/@alstar/studio'
|
|
17
24
|
|
|
@@ -23,6 +30,7 @@ export let studioConfig: types.StudioConfig = {
|
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
const createStudio = async (config: types.StudioConfig) => {
|
|
33
|
+
// const refresher = await createRefresher({ rootdir: ['.', import.meta.dirname] })
|
|
26
34
|
const refresher = await createRefresher({ rootdir: '.' })
|
|
27
35
|
|
|
28
36
|
loadDb('./studio.db')
|
|
@@ -44,26 +52,58 @@ const createStudio = async (config: types.StudioConfig) => {
|
|
|
44
52
|
app.use('*', serveStatic({ root: path.join(rootdir, 'public') }))
|
|
45
53
|
app.use('*', serveStatic({ root: './public' }))
|
|
46
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Require authentication to access Studio
|
|
57
|
+
*/
|
|
58
|
+
app.use('/studio/*', auth)
|
|
59
|
+
|
|
47
60
|
/**
|
|
48
61
|
* Studio API routes
|
|
49
62
|
*/
|
|
50
|
-
app.route('/
|
|
51
|
-
app.route('/
|
|
63
|
+
app.route('/studio/api', apiRoutes)
|
|
64
|
+
app.route('/studio/mcp', mcpRoutes)
|
|
52
65
|
|
|
53
66
|
/**
|
|
54
67
|
* Studio pages
|
|
55
68
|
*/
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
if (adminPages) app.route('/admin', adminPages)
|
|
69
|
+
const studioPages = await fileBasedRouter(path.join(rootdir, 'pages'))
|
|
70
|
+
if (studioPages) app.route('/studio', studioPages)
|
|
59
71
|
|
|
60
72
|
/**
|
|
61
73
|
* User pages
|
|
62
74
|
*/
|
|
63
75
|
const pages = await fileBasedRouter('./pages')
|
|
64
|
-
|
|
65
76
|
if (pages) app.route('/', pages)
|
|
66
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Error pages
|
|
80
|
+
*/
|
|
81
|
+
app.notFound((c) => c.html(ErrorPage()))
|
|
82
|
+
app.onError((err, c) => {
|
|
83
|
+
console.log(err)
|
|
84
|
+
|
|
85
|
+
if (err instanceof HTTPException) {
|
|
86
|
+
// Get the custom response
|
|
87
|
+
const error = err.getResponse()
|
|
88
|
+
return c.html(ErrorPage(err))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return c.notFound()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
app.use(
|
|
95
|
+
'/studio/backups/*',
|
|
96
|
+
serveStatic({
|
|
97
|
+
root: './',
|
|
98
|
+
rewriteRequestPath: (path) => path.replace(/^\/studio\/backups/, '/backups'),
|
|
99
|
+
}),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
// console.log(app.routes)
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Run server
|
|
106
|
+
*/
|
|
67
107
|
const server = serve({
|
|
68
108
|
fetch: app.fetch,
|
|
69
109
|
port: studioConfig.port,
|
package/package.json
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alstar/studio",
|
|
3
|
-
"version": "0.0.0-beta.
|
|
3
|
+
"version": "0.0.0-beta.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.ts",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=23.8"
|
|
8
|
+
},
|
|
6
9
|
"dependencies": {
|
|
7
10
|
"@hono/node-server": "^1.18.1",
|
|
8
11
|
"@starfederation/datastar-sdk": "1.0.0-RC.1",
|
|
9
12
|
"hono": "^4.8.12",
|
|
13
|
+
"@alstar/db": "0.0.0-beta.1",
|
|
10
14
|
"@alstar/refresher": "0.0.0-beta.3",
|
|
11
|
-
"@alstar/ui": "0.0.0-beta.1"
|
|
12
|
-
"@alstar/db": "0.0.0-beta.1"
|
|
15
|
+
"@alstar/ui": "0.0.0-beta.1"
|
|
13
16
|
},
|
|
14
17
|
"devDependencies": {
|
|
15
18
|
"@types/node": "^24.1.0",
|
package/pages/entry/[id].ts
CHANGED
package/pages/error.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { html } from "hono/html";
|
|
2
|
+
import SiteLayout from "../components/SiteLayout.ts";
|
|
3
|
+
import type { HTTPResponseError } from "hono/types";
|
|
4
|
+
|
|
5
|
+
export default ((err?: Error | HTTPResponseError) => {
|
|
6
|
+
|
|
7
|
+
return SiteLayout(html`
|
|
8
|
+
<article>
|
|
9
|
+
<header>Something went wrong</header>
|
|
10
|
+
<p>Try again</p>
|
|
11
|
+
<p>${err?.message || '404 - Not found'}</p>
|
|
12
|
+
</article>
|
|
13
|
+
`, false)
|
|
14
|
+
})
|
package/pages/index.ts
CHANGED
package/pages/login.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { html } from "hono/html";
|
|
2
|
+
import { defineEntry } from "../utils/define.ts";
|
|
3
|
+
import SiteLayout from "../components/SiteLayout.ts";
|
|
4
|
+
|
|
5
|
+
export default defineEntry(c => {
|
|
6
|
+
return SiteLayout(html`
|
|
7
|
+
<div class="login-form">
|
|
8
|
+
<article>
|
|
9
|
+
<header>Login</header>
|
|
10
|
+
<form data-on-submit="@post('/studio/api/auth/login', { contentType: 'form' })">
|
|
11
|
+
<label for="email">Email</label>
|
|
12
|
+
<input id="email" name="email" type="text" placeholder="Email">
|
|
13
|
+
<label for="password">Password</label>
|
|
14
|
+
<input id="password" name="password" type="password" placeholder="Password">
|
|
15
|
+
<br>
|
|
16
|
+
<button style="width: 100%;">Login</button>
|
|
17
|
+
</form>
|
|
18
|
+
</article>
|
|
19
|
+
</div>
|
|
20
|
+
`, false)
|
|
21
|
+
})
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { html } from 'hono/html'
|
|
2
|
+
import { defineEntry } from '../utils/define.ts'
|
|
3
|
+
import SiteLayout from '../components/SiteLayout.ts'
|
|
4
|
+
|
|
5
|
+
export default defineEntry((c) => {
|
|
6
|
+
return SiteLayout(
|
|
7
|
+
html`
|
|
8
|
+
<div class="register-form" style="width: 300px">
|
|
9
|
+
<article>
|
|
10
|
+
<header>Register user</header>
|
|
11
|
+
<form
|
|
12
|
+
data-signals="{ status: 0 }"
|
|
13
|
+
data-on-submit="@post('/studio/api/auth/register', { contentType: 'form' })"
|
|
14
|
+
data-on-signal-patch="patch.status === 200 && window.location.reload()"
|
|
15
|
+
>
|
|
16
|
+
<label for="email">Email</label>
|
|
17
|
+
<input id="email" name="email" type="text" placeholder="Email" />
|
|
18
|
+
<label for="password">Password</label>
|
|
19
|
+
<input
|
|
20
|
+
id="password"
|
|
21
|
+
name="password"
|
|
22
|
+
type="password"
|
|
23
|
+
placeholder="Password"
|
|
24
|
+
/>
|
|
25
|
+
<br />
|
|
26
|
+
<button style="width: 100%;">Register</button>
|
|
27
|
+
</form>
|
|
28
|
+
</article>
|
|
29
|
+
</div>
|
|
30
|
+
`,
|
|
31
|
+
false,
|
|
32
|
+
)
|
|
33
|
+
})
|
package/pages/settings.ts
CHANGED
|
@@ -1,24 +1,16 @@
|
|
|
1
1
|
#settings {
|
|
2
|
-
|
|
3
|
-
padding: 0;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
li {
|
|
7
|
-
width: 100%;
|
|
8
|
-
display: grid;
|
|
9
|
-
align-items: center;
|
|
10
|
-
grid-template-columns: 15% 1fr auto;
|
|
11
|
-
gap: 1rem;
|
|
12
|
-
border-bottom: 1px solid var(--pico-form-element-border-color);
|
|
13
|
-
margin-bottom: 0;
|
|
14
|
-
padding: 1rem 0;
|
|
15
|
-
|
|
2
|
+
tr {
|
|
16
3
|
input {
|
|
4
|
+
width: 100%;
|
|
17
5
|
font-family: var(--pico-font-family-monospace);
|
|
18
6
|
}
|
|
19
7
|
|
|
20
8
|
* {
|
|
21
|
-
margin
|
|
9
|
+
margin: 0;
|
|
22
10
|
}
|
|
23
11
|
}
|
|
24
12
|
}
|
|
13
|
+
|
|
14
|
+
.login-form {
|
|
15
|
+
width: 100%;
|
|
16
|
+
}
|
|
@@ -22,7 +22,7 @@ class SortableList extends HTMLElement {
|
|
|
22
22
|
searchParams.set('id', child.dataset.id)
|
|
23
23
|
searchParams.set('sort-order', idx)
|
|
24
24
|
|
|
25
|
-
await fetch(`/
|
|
25
|
+
await fetch(`/studio/api/sort-order?${searchParams.toString()}`, {
|
|
26
26
|
method: 'post',
|
|
27
27
|
})
|
|
28
28
|
})
|
package/public/studio/main.css
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* @import './../node_modules/@alstar/ui/red.css'; */
|
|
2
1
|
@import 'https://esm.sh/@alstar/ui/red.css';
|
|
3
2
|
@import './css/admin-panel.css';
|
|
4
3
|
@import './css/blocks-field.css';
|
|
@@ -16,6 +15,7 @@ body {
|
|
|
16
15
|
padding: 40px;
|
|
17
16
|
height: 100vh;
|
|
18
17
|
overflow: auto;
|
|
18
|
+
width: 100%;
|
|
19
19
|
|
|
20
20
|
section {
|
|
21
21
|
max-width: 900px;
|
|
@@ -24,6 +24,10 @@ body {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
button svg, a svg {
|
|
28
|
+
pointer-events: none;
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
.entry > .fields > .block {
|
|
28
32
|
padding-block: var(--pico-spacing);
|
|
29
33
|
|
package/public/studio/main.js
CHANGED
|
@@ -4,6 +4,19 @@ import './js/markdown-editor.js'
|
|
|
4
4
|
import './js/sortable-list.js'
|
|
5
5
|
|
|
6
6
|
barba.init({
|
|
7
|
+
debug: true,
|
|
7
8
|
cacheIgnore: true,
|
|
8
9
|
views: [{ namespace: 'default' }],
|
|
9
10
|
})
|
|
11
|
+
|
|
12
|
+
// client side script
|
|
13
|
+
// const eventSource = new EventSource('http://localhost:5432')
|
|
14
|
+
// eventSource.onmessage = ({ data }) => {
|
|
15
|
+
// const delay = data.split(' - ').at(-1) === 'true'
|
|
16
|
+
// setTimeout(() => window.location.reload(), delay ? 0 : 1000)
|
|
17
|
+
// }
|
|
18
|
+
|
|
19
|
+
// console.log(
|
|
20
|
+
// '%c REFRESHER ACTIVE ',
|
|
21
|
+
// 'color: green; background: lightgreen; border-radius: 2px'
|
|
22
|
+
// )
|