@alstar/studio 0.0.0-beta.4 → 0.0.0-beta.6
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 +74 -0
- package/api/block.ts +39 -21
- package/api/index.ts +9 -1
- package/api/mcp.ts +53 -0
- package/bin/alstar.ts +42 -0
- package/components/{AdminPanel/AdminPanel.ts → AdminPanel.ts} +22 -27
- package/components/Block.ts +51 -112
- package/components/Entries.ts +17 -10
- package/components/Entry.ts +9 -15
- package/components/Settings.ts +98 -0
- package/components/fields/Blocks.ts +118 -0
- package/components/fields/Text.ts +42 -0
- package/components/fields/index.ts +4 -0
- package/components/icons.ts +97 -0
- package/components/index.ts +1 -1
- package/components/layout.ts +8 -5
- package/index.ts +48 -20
- package/package.json +2 -1
- package/public/admin-panel.css +90 -0
- package/public/blocks.css +53 -0
- package/public/main.css +11 -1
- package/public/main.js +4 -0
- package/public/settings.css +24 -0
- package/queries/block-with-children.ts +74 -0
- package/queries/block.ts +287 -0
- package/queries/db-types.ts +15 -0
- package/queries/getBlockTrees-2.ts +0 -0
- package/queries/getBlockTrees.ts +316 -0
- package/queries/getBlocks.ts +214 -0
- package/queries/index.ts +2 -98
- package/queries/structure-types.ts +97 -0
- package/schemas.ts +15 -54
- package/types.ts +95 -5
- package/utils/buildBlocksTree.ts +4 -4
- package/utils/define.ts +21 -5
- package/utils/file-based-router.ts +9 -1
- package/utils/get-config.ts +8 -9
- package/utils/get-or-create-row.ts +28 -0
- package/utils/startup-log.ts +9 -0
- package/components/AdminPanel/AdminPanel.css +0 -59
- package/components/Field.ts +0 -164
- package/components/Fields.ts +0 -43
- /package/{components/Entry.css → public/entry.css} +0 -0
package/public/main.css
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/* @import './../node_modules/@alstar/ui/red.css'; */
|
|
2
2
|
@import 'https://esm.sh/@alstar/ui/red.css';
|
|
3
|
+
@import './admin-panel.css';
|
|
4
|
+
@import './entry.css';
|
|
5
|
+
@import './blocks.css';
|
|
6
|
+
@import './settings.css';
|
|
3
7
|
|
|
4
8
|
body {
|
|
5
9
|
min-height: 100vh;
|
|
@@ -15,6 +19,8 @@ body {
|
|
|
15
19
|
|
|
16
20
|
> main {
|
|
17
21
|
padding: 40px;
|
|
22
|
+
height: 100vh;
|
|
23
|
+
overflow: auto;
|
|
18
24
|
|
|
19
25
|
section {
|
|
20
26
|
max-width: 900px;
|
|
@@ -90,4 +96,8 @@ div.ink-mde {
|
|
|
90
96
|
.disclamer {
|
|
91
97
|
display: flex;
|
|
92
98
|
justify-content: center;
|
|
93
|
-
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.text-secondary {
|
|
102
|
+
color: var(--pico-secondary);
|
|
103
|
+
}
|
package/public/main.js
CHANGED
|
@@ -3,6 +3,7 @@ import Sortable from 'sortablejs'
|
|
|
3
3
|
// import { wrap } from 'ink-mde'
|
|
4
4
|
|
|
5
5
|
barba.init({
|
|
6
|
+
cacheIgnore: true,
|
|
6
7
|
views: [
|
|
7
8
|
{
|
|
8
9
|
namespace: 'default',
|
|
@@ -19,11 +20,14 @@ function init() {
|
|
|
19
20
|
const els = document.querySelectorAll('[data-sortable]')
|
|
20
21
|
|
|
21
22
|
els.forEach((el) => {
|
|
23
|
+
const id = el.dataset.sortable
|
|
24
|
+
|
|
22
25
|
var sortable = Sortable.create(el, {
|
|
23
26
|
delay: 0,
|
|
24
27
|
animation: 250,
|
|
25
28
|
dragClass: 'sortable-drag',
|
|
26
29
|
easing: 'ease-in-out',
|
|
30
|
+
handle: `[data-handle="${id}"]`,
|
|
27
31
|
})
|
|
28
32
|
})
|
|
29
33
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#settings {
|
|
2
|
+
ul {
|
|
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
|
+
|
|
16
|
+
input {
|
|
17
|
+
font-family: var(--pico-font-family-monospace);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
* {
|
|
21
|
+
margin-bottom: 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { sql } from '../utils/sql.ts'
|
|
2
|
+
|
|
3
|
+
export const blockWithChildren = sql`
|
|
4
|
+
with recursive
|
|
5
|
+
block_tree as (
|
|
6
|
+
-- Start from the root block you want
|
|
7
|
+
select
|
|
8
|
+
id,
|
|
9
|
+
name,
|
|
10
|
+
label,
|
|
11
|
+
type,
|
|
12
|
+
sort_order,
|
|
13
|
+
value,
|
|
14
|
+
options,
|
|
15
|
+
status,
|
|
16
|
+
parent_id,
|
|
17
|
+
0 as depth
|
|
18
|
+
from
|
|
19
|
+
blocks
|
|
20
|
+
where
|
|
21
|
+
id = ? -- <-- put your starting block id here
|
|
22
|
+
union all
|
|
23
|
+
-- Recursively select children
|
|
24
|
+
select
|
|
25
|
+
b.id,
|
|
26
|
+
b.name,
|
|
27
|
+
b.label,
|
|
28
|
+
b.type,
|
|
29
|
+
b.sort_order,
|
|
30
|
+
b.value,
|
|
31
|
+
b.options,
|
|
32
|
+
b.status,
|
|
33
|
+
b.parent_id,
|
|
34
|
+
bt.depth + 1
|
|
35
|
+
from
|
|
36
|
+
blocks b
|
|
37
|
+
inner join block_tree bt on b.parent_id = bt.id
|
|
38
|
+
)
|
|
39
|
+
select
|
|
40
|
+
*
|
|
41
|
+
from
|
|
42
|
+
block_tree
|
|
43
|
+
order by
|
|
44
|
+
depth,
|
|
45
|
+
sort_order;
|
|
46
|
+
`
|
|
47
|
+
|
|
48
|
+
export const deleteBlockWithChildren = sql`
|
|
49
|
+
with recursive
|
|
50
|
+
block_tree as (
|
|
51
|
+
-- start from the root block you want to delete
|
|
52
|
+
select
|
|
53
|
+
id
|
|
54
|
+
from
|
|
55
|
+
blocks
|
|
56
|
+
where
|
|
57
|
+
id = ? -- <-- put your root block id here
|
|
58
|
+
union all
|
|
59
|
+
-- recursively select children
|
|
60
|
+
select
|
|
61
|
+
b.id
|
|
62
|
+
from
|
|
63
|
+
blocks b
|
|
64
|
+
inner join block_tree bt on b.parent_id = bt.id
|
|
65
|
+
)
|
|
66
|
+
delete from blocks
|
|
67
|
+
where
|
|
68
|
+
id in (
|
|
69
|
+
select
|
|
70
|
+
id
|
|
71
|
+
from
|
|
72
|
+
block_tree
|
|
73
|
+
);
|
|
74
|
+
`
|
package/queries/block.ts
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { db } from '@alstar/db'
|
|
2
|
+
import { sql } from '../utils/sql.ts'
|
|
3
|
+
import { type DBBlockResult } from '../types.ts'
|
|
4
|
+
|
|
5
|
+
function buildForest(blocks: DBBlockResult[]): DBBlockResult[] {
|
|
6
|
+
const map = new Map<number, DBBlockResult>()
|
|
7
|
+
const roots: DBBlockResult[] = []
|
|
8
|
+
|
|
9
|
+
for (const block of blocks) {
|
|
10
|
+
block.children = []
|
|
11
|
+
map.set(block.id, block)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
for (const block of blocks) {
|
|
15
|
+
if (block.parent_id === null) {
|
|
16
|
+
roots.push(block)
|
|
17
|
+
} else {
|
|
18
|
+
const parent = map.get(block.parent_id)
|
|
19
|
+
if (parent) parent.children!.push(block)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Sort children by sort_order recursively
|
|
24
|
+
const sortChildren = (node: DBBlockResult) => {
|
|
25
|
+
node.children!.sort((a, b) => a.sort_order - b.sort_order)
|
|
26
|
+
node.children!.forEach(sortChildren)
|
|
27
|
+
}
|
|
28
|
+
roots.forEach(sortChildren)
|
|
29
|
+
|
|
30
|
+
return roots
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function buildTree(blocks: DBBlockResult[]): DBBlockResult {
|
|
34
|
+
const map = new Map<number, DBBlockResult>()
|
|
35
|
+
const roots: DBBlockResult[] = []
|
|
36
|
+
|
|
37
|
+
for (const block of blocks) {
|
|
38
|
+
block.children = []
|
|
39
|
+
map.set(block.id, block)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const block of blocks) {
|
|
43
|
+
if (block.parent_id === null) {
|
|
44
|
+
roots.push(block)
|
|
45
|
+
} else {
|
|
46
|
+
const parent = map.get(block.parent_id)
|
|
47
|
+
if (parent) parent.children!.push(block)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Sort children by sort_order recursively
|
|
52
|
+
const sortChildren = (node: DBBlockResult) => {
|
|
53
|
+
node.children!.sort((a, b) => a.sort_order - b.sort_order)
|
|
54
|
+
node.children!.forEach(sortChildren)
|
|
55
|
+
}
|
|
56
|
+
roots.forEach(sortChildren)
|
|
57
|
+
|
|
58
|
+
return roots[0]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function transformBlocksTree(
|
|
62
|
+
block: DBBlockResult,
|
|
63
|
+
isBlocksChild?: boolean,
|
|
64
|
+
): DBBlockResult {
|
|
65
|
+
const fields: Record<string, DBBlockResult> = {}
|
|
66
|
+
let hasFields = false
|
|
67
|
+
|
|
68
|
+
for (const child of block.children ?? []) {
|
|
69
|
+
const transformedChild = transformBlocksTree(
|
|
70
|
+
child,
|
|
71
|
+
child.type === 'blocks',
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if (!isBlocksChild) {
|
|
75
|
+
hasFields = true
|
|
76
|
+
fields[transformedChild.name] = transformedChild
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if(hasFields) {
|
|
81
|
+
block.fields = fields
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!isBlocksChild) {
|
|
85
|
+
delete block.children
|
|
86
|
+
} else {
|
|
87
|
+
delete block.fields
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return block
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function transformForest(blocks: DBBlockResult[]): DBBlockResult[] {
|
|
94
|
+
return blocks.map((block) => transformBlocksTree(block))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function rootQuery(filterSql: string, depthLimit?: number) {
|
|
98
|
+
const depthLimitClause =
|
|
99
|
+
depthLimit !== undefined ? `WHERE d.depth + 1 <= ${depthLimit}` : ''
|
|
100
|
+
|
|
101
|
+
return sql`
|
|
102
|
+
with recursive
|
|
103
|
+
ancestors as (
|
|
104
|
+
select
|
|
105
|
+
id,
|
|
106
|
+
created_at,
|
|
107
|
+
updated_at,
|
|
108
|
+
name,
|
|
109
|
+
label,
|
|
110
|
+
type,
|
|
111
|
+
sort_order,
|
|
112
|
+
value,
|
|
113
|
+
options,
|
|
114
|
+
status,
|
|
115
|
+
parent_id,
|
|
116
|
+
0 as depth
|
|
117
|
+
from
|
|
118
|
+
blocks
|
|
119
|
+
where
|
|
120
|
+
${filterSql}
|
|
121
|
+
union all
|
|
122
|
+
select
|
|
123
|
+
b.id,
|
|
124
|
+
b.created_at,
|
|
125
|
+
b.updated_at,
|
|
126
|
+
b.name,
|
|
127
|
+
b.label,
|
|
128
|
+
b.type,
|
|
129
|
+
b.sort_order,
|
|
130
|
+
b.value,
|
|
131
|
+
b.options,
|
|
132
|
+
b.status,
|
|
133
|
+
b.parent_id,
|
|
134
|
+
a.depth + 1
|
|
135
|
+
from
|
|
136
|
+
blocks b
|
|
137
|
+
inner join ancestors a on b.id = a.parent_id
|
|
138
|
+
),
|
|
139
|
+
roots as (
|
|
140
|
+
select
|
|
141
|
+
id,
|
|
142
|
+
created_at,
|
|
143
|
+
updated_at,
|
|
144
|
+
name,
|
|
145
|
+
label,
|
|
146
|
+
type,
|
|
147
|
+
sort_order,
|
|
148
|
+
value,
|
|
149
|
+
options,
|
|
150
|
+
status,
|
|
151
|
+
parent_id,
|
|
152
|
+
0 as depth
|
|
153
|
+
from
|
|
154
|
+
ancestors
|
|
155
|
+
where
|
|
156
|
+
parent_id is null
|
|
157
|
+
),
|
|
158
|
+
descendants as (
|
|
159
|
+
select
|
|
160
|
+
id,
|
|
161
|
+
created_at,
|
|
162
|
+
updated_at,
|
|
163
|
+
name,
|
|
164
|
+
label,
|
|
165
|
+
type,
|
|
166
|
+
sort_order,
|
|
167
|
+
value,
|
|
168
|
+
options,
|
|
169
|
+
status,
|
|
170
|
+
parent_id,
|
|
171
|
+
depth
|
|
172
|
+
from
|
|
173
|
+
roots
|
|
174
|
+
union all
|
|
175
|
+
select
|
|
176
|
+
b.id,
|
|
177
|
+
b.created_at,
|
|
178
|
+
b.updated_at,
|
|
179
|
+
b.name,
|
|
180
|
+
b.label,
|
|
181
|
+
b.type,
|
|
182
|
+
b.sort_order,
|
|
183
|
+
b.value,
|
|
184
|
+
b.options,
|
|
185
|
+
b.status,
|
|
186
|
+
b.parent_id,
|
|
187
|
+
d.depth + 1
|
|
188
|
+
from
|
|
189
|
+
blocks b
|
|
190
|
+
inner join descendants d on b.parent_id = d.id ${depthLimitClause}
|
|
191
|
+
)
|
|
192
|
+
select
|
|
193
|
+
*
|
|
194
|
+
from
|
|
195
|
+
descendants
|
|
196
|
+
order by
|
|
197
|
+
sort_order,
|
|
198
|
+
id;
|
|
199
|
+
`
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function buildFilterSql(params: Record<string, any>) {
|
|
203
|
+
const entries = Object.entries(params)
|
|
204
|
+
const filterSql = entries
|
|
205
|
+
.map(([key, value]) =>
|
|
206
|
+
value === null ? `${key} is null` : `${key} = :${key}`,
|
|
207
|
+
)
|
|
208
|
+
.join(' and ')
|
|
209
|
+
|
|
210
|
+
let sqlParams: Record<keyof typeof params, any> = {}
|
|
211
|
+
|
|
212
|
+
for (const param in params) {
|
|
213
|
+
if (params[param] !== null) {
|
|
214
|
+
sqlParams[param] = params[param]
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return { filterSql, sqlParams }
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function roots(
|
|
222
|
+
params: Record<string, any>,
|
|
223
|
+
options?: {
|
|
224
|
+
depth?: number
|
|
225
|
+
},
|
|
226
|
+
): DBBlockResult[] | [] {
|
|
227
|
+
const { filterSql, sqlParams } = buildFilterSql(params)
|
|
228
|
+
|
|
229
|
+
const query = rootQuery(filterSql, options?.depth)
|
|
230
|
+
const rows = db.database
|
|
231
|
+
.prepare(query)
|
|
232
|
+
.all(sqlParams) as unknown as DBBlockResult[]
|
|
233
|
+
|
|
234
|
+
if (!rows.length) return []
|
|
235
|
+
|
|
236
|
+
const forest = buildForest(rows)
|
|
237
|
+
|
|
238
|
+
return transformForest(forest)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function root(
|
|
242
|
+
params: Record<string, any>,
|
|
243
|
+
options?: { depth?: number },
|
|
244
|
+
): DBBlockResult | null {
|
|
245
|
+
const { filterSql, sqlParams } = buildFilterSql(params)
|
|
246
|
+
|
|
247
|
+
const query = rootQuery(filterSql, options?.depth)
|
|
248
|
+
const rows = db.database
|
|
249
|
+
.prepare(query)
|
|
250
|
+
.all(sqlParams) as unknown as DBBlockResult[]
|
|
251
|
+
|
|
252
|
+
if (!rows.length) return null
|
|
253
|
+
|
|
254
|
+
const tree = buildTree(rows)
|
|
255
|
+
|
|
256
|
+
return transformBlocksTree(tree)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function block(params: Record<string, any>) {
|
|
260
|
+
const { filterSql, sqlParams } = buildFilterSql(params)
|
|
261
|
+
|
|
262
|
+
const query = sql`
|
|
263
|
+
select
|
|
264
|
+
*
|
|
265
|
+
from
|
|
266
|
+
blocks
|
|
267
|
+
where
|
|
268
|
+
${filterSql}
|
|
269
|
+
`
|
|
270
|
+
|
|
271
|
+
return db.database.prepare(query).get(sqlParams) as unknown as DBBlockResult
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function blocks(params: Record<string, any>) {
|
|
275
|
+
const { filterSql, sqlParams } = buildFilterSql(params)
|
|
276
|
+
|
|
277
|
+
const query = sql`
|
|
278
|
+
select
|
|
279
|
+
*
|
|
280
|
+
from
|
|
281
|
+
blocks
|
|
282
|
+
where
|
|
283
|
+
${filterSql}
|
|
284
|
+
`
|
|
285
|
+
|
|
286
|
+
return db.database.prepare(query).all(sqlParams) as unknown as DBBlockResult[]
|
|
287
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// db-types.ts
|
|
2
|
+
export type DBBase = {
|
|
3
|
+
id: number;
|
|
4
|
+
created_at: string;
|
|
5
|
+
updated_at: string;
|
|
6
|
+
name: string;
|
|
7
|
+
label: string;
|
|
8
|
+
type: string;
|
|
9
|
+
sort_order: number;
|
|
10
|
+
value: string | null;
|
|
11
|
+
options: any | null;
|
|
12
|
+
status: string;
|
|
13
|
+
parent_id: number | null;
|
|
14
|
+
depth: number;
|
|
15
|
+
};
|
|
File without changes
|