@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.
Files changed (43) hide show
  1. package/api/api-key.ts +74 -0
  2. package/api/block.ts +39 -21
  3. package/api/index.ts +9 -1
  4. package/api/mcp.ts +53 -0
  5. package/bin/alstar.ts +42 -0
  6. package/components/{AdminPanel/AdminPanel.ts → AdminPanel.ts} +22 -27
  7. package/components/Block.ts +51 -112
  8. package/components/Entries.ts +17 -10
  9. package/components/Entry.ts +9 -15
  10. package/components/Settings.ts +98 -0
  11. package/components/fields/Blocks.ts +118 -0
  12. package/components/fields/Text.ts +42 -0
  13. package/components/fields/index.ts +4 -0
  14. package/components/icons.ts +97 -0
  15. package/components/index.ts +1 -1
  16. package/components/layout.ts +8 -5
  17. package/index.ts +48 -20
  18. package/package.json +2 -1
  19. package/public/admin-panel.css +90 -0
  20. package/public/blocks.css +53 -0
  21. package/public/main.css +11 -1
  22. package/public/main.js +4 -0
  23. package/public/settings.css +24 -0
  24. package/queries/block-with-children.ts +74 -0
  25. package/queries/block.ts +287 -0
  26. package/queries/db-types.ts +15 -0
  27. package/queries/getBlockTrees-2.ts +0 -0
  28. package/queries/getBlockTrees.ts +316 -0
  29. package/queries/getBlocks.ts +214 -0
  30. package/queries/index.ts +2 -98
  31. package/queries/structure-types.ts +97 -0
  32. package/schemas.ts +15 -54
  33. package/types.ts +95 -5
  34. package/utils/buildBlocksTree.ts +4 -4
  35. package/utils/define.ts +21 -5
  36. package/utils/file-based-router.ts +9 -1
  37. package/utils/get-config.ts +8 -9
  38. package/utils/get-or-create-row.ts +28 -0
  39. package/utils/startup-log.ts +9 -0
  40. package/components/AdminPanel/AdminPanel.css +0 -59
  41. package/components/Field.ts +0 -164
  42. package/components/Fields.ts +0 -43
  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
+ `
@@ -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