@alstar/studio 0.0.0-beta.1 → 0.0.0-beta.10

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 (58) hide show
  1. package/api/api-key.ts +73 -0
  2. package/api/backup.ts +38 -0
  3. package/api/block.ts +96 -30
  4. package/api/index.ts +12 -1
  5. package/api/mcp.ts +53 -0
  6. package/components/AdminPanel.ts +74 -0
  7. package/components/Backup.ts +10 -0
  8. package/components/BlockFieldRenderer.ts +121 -0
  9. package/components/BlockRenderer.ts +22 -0
  10. package/components/Entries.ts +19 -11
  11. package/components/Entry.ts +13 -21
  12. package/components/FieldRenderer.ts +35 -0
  13. package/components/Render.ts +46 -0
  14. package/components/Settings.ts +101 -0
  15. package/components/{layout.ts → SiteLayout.ts} +22 -17
  16. package/components/fields/Markdown.ts +44 -0
  17. package/components/fields/Text.ts +42 -0
  18. package/components/fields/index.ts +4 -0
  19. package/components/icons.ts +100 -7
  20. package/index.ts +66 -34
  21. package/package.json +8 -8
  22. package/pages/entry/[id].ts +17 -0
  23. package/{components → pages}/index.ts +7 -4
  24. package/pages/settings.ts +10 -0
  25. package/public/studio/css/admin-panel.css +103 -0
  26. package/public/studio/css/blocks-field.css +53 -0
  27. package/public/studio/css/settings.css +24 -0
  28. package/public/studio/js/markdown-editor.js +34 -0
  29. package/public/studio/js/sortable-list.js +50 -0
  30. package/public/studio/main.css +161 -0
  31. package/public/studio/main.js +9 -0
  32. package/queries/block-with-children.ts +74 -0
  33. package/queries/block.ts +289 -0
  34. package/queries/db-types.ts +15 -0
  35. package/queries/getBlockTrees-2.ts +0 -0
  36. package/queries/getBlockTrees.ts +316 -0
  37. package/queries/getBlocks.ts +214 -0
  38. package/queries/index.ts +2 -98
  39. package/queries/structure-types.ts +97 -0
  40. package/readme.md +205 -0
  41. package/schemas.ts +15 -54
  42. package/types.ts +133 -5
  43. package/utils/buildBlocksTree.ts +4 -4
  44. package/utils/define.ts +41 -0
  45. package/utils/file-based-router.ts +11 -2
  46. package/utils/get-config.ts +8 -9
  47. package/utils/get-or-create-row.ts +41 -0
  48. package/utils/html.ts +247 -0
  49. package/utils/startup-log.ts +10 -0
  50. package/components/AdminPanel/AdminPanel.css +0 -59
  51. package/components/AdminPanel/AdminPanel.ts +0 -57
  52. package/components/Block.ts +0 -116
  53. package/components/Entry.css +0 -7
  54. package/components/Field.ts +0 -164
  55. package/components/Fields.ts +0 -43
  56. package/public/main.css +0 -92
  57. package/public/main.js +0 -43
  58. /package/public/{favicon.svg → studio/favicon.svg} +0 -0
@@ -1,116 +0,0 @@
1
- import { html } from 'hono/html'
2
- import { query } from '../queries/index.ts'
3
- import Fields from './Fields.ts'
4
- import * as icons from './icons.ts'
5
- import { type Block } from '../types.ts'
6
- import Field from './Field.ts'
7
- import {
8
- buildStructurePath,
9
- type StructurePath,
10
- } from '../utils/build-structure-path.ts'
11
-
12
- export default (
13
- entryId: string | number,
14
- id: string | number,
15
- fieldStructure: Block,
16
- structurePath: StructurePath,
17
- ) => {
18
- const data = query.block({ id: id.toString() })
19
- const blocks = query.blocks({ parent: id })
20
-
21
- if (!data) return html`<p>No block data</p>`
22
-
23
- const fieldStructurePath = buildStructurePath(fieldStructure, structurePath)
24
-
25
- return html`
26
-
27
- <section class="block">
28
- <header>
29
- <h5>${fieldStructure.label}</h5>
30
-
31
- <form
32
- data-on-submit="@post('/admin/api/new-block', { contentType: 'form' })"
33
- >
34
- <input type="hidden" name="parent_block_id" value="${id}" />
35
- <input
36
- type="hidden"
37
- name="sort_order"
38
- value="${blocks?.length || 0}"
39
- />
40
-
41
- <fieldset role="group">
42
- <select name="block">
43
- ${fieldStructure.fields?.map((field) => {
44
- return html`<option
45
- value="entry_id:${entryId};type:${field.type};name:${field.name};label:${field.label};sort_order:${blocks?.length ||
46
- 0}"
47
- >
48
- ${field.label}
49
- </option>`
50
- })}
51
- </select>
52
-
53
- <button
54
- class="outline"
55
- style="padding: 0 1rem"
56
- data-tooltip="New entry"
57
- data-placement="right"
58
- type="submit"
59
- >
60
- Add
61
- </button>
62
- </fieldset>
63
- </form>
64
- </header>
65
-
66
- <div data-sortable="${id}">
67
- ${blocks?.map((block, idx) => {
68
- const structure = fieldStructure.fields?.find(
69
- (field) => field.name === block.name,
70
- )
71
-
72
- if (!structure) return
73
-
74
- return html`
75
- <article
76
- data-signals="{ open${block.id}: true }"
77
- >
78
- <header>
79
- <h6>${structure.label}</h6>
80
- <div>
81
- <button
82
- data-on-click="$open${block.id} = !$open${block.id}"
83
- style="margin-right: 0.5rem"
84
- data-style-rotate="$open${block.id} ? '0deg' : '180deg'"
85
- class="shadow"
86
- >
87
- ${icons.chevron}
88
- </button>
89
- <button class="shadow">${icons.bars}</button>
90
- </div>
91
- </header>
92
-
93
- <div data-show="$open${block.id}">
94
- ${structure.fields
95
- ? Fields({
96
- entryId,
97
- parentId: block.id,
98
- blockStructure: structure,
99
- structurePath: [...fieldStructurePath, structure.name],
100
- })
101
- : Field({
102
- entryId,
103
- parentId: block.id,
104
- blockStructure: structure,
105
- sortOrder: idx,
106
- structurePath: [...fieldStructurePath, structure.name],
107
- })}
108
-
109
- </div>
110
- </article>
111
- `
112
- })}
113
- </div>
114
- </section>
115
- `
116
- }
@@ -1,7 +0,0 @@
1
- .entry {
2
- form {
3
- display: flex;
4
- flex-direction: column;
5
- align-items: flex-start;
6
- }
7
- }
@@ -1,164 +0,0 @@
1
- import { html } from 'hono/html'
2
- import { type Block } from '../types.ts'
3
- import { type HtmlEscapedString } from 'hono/utils/html'
4
- import { query } from '../queries/index.ts'
5
- import { db } from '@alstar/db'
6
- import BlockComponent from './Block.ts'
7
- import {
8
- buildStructurePath,
9
- type StructurePath,
10
- } from '../utils/build-structure-path.ts'
11
-
12
- function getData(parentId: string | number, field: Block, sortOrder: number) {
13
- const data = query.block({
14
- parent: parentId?.toString() || null,
15
- name: field.name,
16
- sort_order: sortOrder.toString(),
17
- })
18
-
19
- if (!data) {
20
- const change = db.insertInto('blocks', {
21
- name: field.name?.toString(),
22
- label: field.label?.toString(),
23
- type: field.type?.toString(),
24
- sort_order: sortOrder,
25
- parent_block_id: parentId,
26
- })
27
-
28
- return query.block({ id: change.lastInsertRowid.toString() })
29
- }
30
-
31
- return data
32
- }
33
-
34
- const Field = (props: {
35
- entryId: string | number
36
- parentId: string | number
37
- blockStructure: Block
38
- sortOrder?: number
39
- structurePath: StructurePath
40
- }): HtmlEscapedString | Promise<HtmlEscapedString> => {
41
- const {
42
- entryId,
43
- parentId,
44
- blockStructure,
45
- sortOrder = 0,
46
- structurePath,
47
- } = props
48
-
49
- const data = getData(parentId, blockStructure, sortOrder)
50
-
51
- if (!data) return html`<p>No block</p>`
52
-
53
- const fieldStructurePath = buildStructurePath(blockStructure, structurePath)
54
-
55
- return html`
56
- ${blockStructure.type === 'slug' &&
57
- html`
58
- <form
59
- data-on-input="@patch('/admin/api/block', { contentType: 'form' })"
60
- >
61
- <label for="block-${data.id}">${blockStructure.label}</label>
62
- <input
63
- id="block-${data.id}"
64
- name="value"
65
- type="text"
66
- value="${data.value}"
67
- />
68
-
69
- <input type="hidden" name="type" value="${blockStructure.type}" />
70
- <input type="hidden" name="id" value="${data.id}" />
71
- <input type="hidden" name="entryId" value="${entryId}" />
72
- <input type="hidden" name="parentId" value="${parentId}" />
73
- <input type="hidden" name="sort_order" value="${sortOrder}" />
74
- <input
75
- type="hidden"
76
- name="path"
77
- value="${fieldStructurePath.join('.')}"
78
- />
79
- </form>
80
- `}
81
- ${blockStructure.type === 'text' &&
82
- html`
83
- <form
84
- data-on-input="@patch('/admin/api/block', { contentType: 'form' })"
85
- >
86
- <label for="block-${data.id}">${blockStructure.label}</label>
87
- <input
88
- id="block-${data.id}"
89
- name="value"
90
- type="text"
91
- value="${data.value}"
92
- />
93
-
94
- <input type="hidden" name="type" value="${blockStructure.type}" />
95
- <input type="hidden" name="id" value="${data.id}" />
96
- <input type="hidden" name="entryId" value="${entryId}" />
97
- <input type="hidden" name="parentId" value="${parentId}" />
98
- <input type="hidden" name="sort_order" value="${sortOrder}" />
99
- <input
100
- type="hidden"
101
- name="path"
102
- value="${fieldStructurePath.join('.')}"
103
- />
104
- </form>
105
- `}
106
- ${blockStructure.type === 'image' &&
107
- html`
108
- <form
109
- data-on-input="@patch('/admin/api/block', { contentType: 'form' })"
110
- >
111
- <label for="block-${data.id}">${blockStructure.label}</label>
112
- <input
113
- id="block-${data.id}"
114
- name="value"
115
- type="text"
116
- value="${data.value}"
117
- />
118
-
119
- <input type="hidden" name="type" value="${blockStructure.type}" />
120
- <input type="hidden" name="id" value="${data.id}" />
121
- <input type="hidden" name="entryId" value="${entryId}" />
122
- <input type="hidden" name="parentId" value="${parentId}" />
123
- <input type="hidden" name="sort_order" value="${sortOrder}" />
124
- <input
125
- type="hidden"
126
- name="path"
127
- value="${fieldStructurePath.join('.')}"
128
- />
129
- </form>
130
- `}
131
- ${blockStructure.type === 'markdown' &&
132
- html`
133
- <form
134
- data-on-input="@patch('/admin/api/block', { contentType: 'form' })"
135
- >
136
- <label for="block-${data.id}">${blockStructure.label}</label>
137
-
138
- <textarea></textarea>
139
-
140
- <!-- <input
141
- id="block-${data.id}"
142
- name="value"
143
- type="text"
144
- value="${data.value}"
145
- /> -->
146
-
147
- <input type="hidden" name="type" value="${blockStructure.type}" />
148
- <input type="hidden" name="id" value="${data.id}" />
149
- <input type="hidden" name="entryId" value="${entryId}" />
150
- <input type="hidden" name="parentId" value="${parentId}" />
151
- <input type="hidden" name="sort_order" value="${sortOrder}" />
152
- <input
153
- type="hidden"
154
- name="path"
155
- value="${fieldStructurePath.join('.')}"
156
- />
157
- </form>
158
- `}
159
- ${blockStructure.type === 'blocks' &&
160
- BlockComponent(entryId, data.id, blockStructure, fieldStructurePath)}
161
- `
162
- }
163
-
164
- export default Field
@@ -1,43 +0,0 @@
1
- import { html } from 'hono/html'
2
- import { type Block } from '../types.ts'
3
- import { type HtmlEscapedString } from 'hono/utils/html'
4
- import Field from './Field.ts'
5
- import {
6
- buildStructurePath,
7
- type StructurePath,
8
- } from '../utils/build-structure-path.ts'
9
-
10
- const Fields = (props: {
11
- entryId: string | number
12
- parentId: string | number
13
- blockStructure: Block
14
- structurePath: StructurePath
15
- }): HtmlEscapedString | Promise<HtmlEscapedString> => {
16
- const updatedPath = buildStructurePath(
17
- props.blockStructure,
18
- props.structurePath,
19
- )
20
-
21
- return html`
22
- <div class="fields">
23
- ${props.blockStructure.fields
24
- ? props.blockStructure.fields?.map((field, idx) => {
25
- return Field({
26
- entryId: props.entryId,
27
- parentId: props.parentId,
28
- blockStructure: field,
29
- sortOrder: idx,
30
- structurePath: [...updatedPath, 'fields'],
31
- })
32
- })
33
- : Field({
34
- entryId: props.entryId,
35
- parentId: props.parentId,
36
- blockStructure: props.blockStructure,
37
- structurePath: updatedPath,
38
- })}
39
- </div>
40
- `
41
- }
42
-
43
- export default Fields
package/public/main.css DELETED
@@ -1,92 +0,0 @@
1
- @import './../node_modules/@alstar/ui/red.css';
2
-
3
- body {
4
- min-height: 100vh;
5
- }
6
-
7
- body {
8
- padding: 0;
9
- min-height: inherit;
10
-
11
- display: grid;
12
- grid-template-columns: auto 1fr;
13
- align-content: baseline;
14
-
15
- > main {
16
- padding: 40px;
17
-
18
- section {
19
- max-width: 900px;
20
- margin: 0 auto;
21
- }
22
- }
23
- }
24
-
25
- .entry > .fields > .block {
26
- padding-block: var(--pico-spacing);
27
-
28
- > header {
29
- margin-block: var(--pico-block-spacing-vertical);
30
- }
31
- }
32
-
33
- .block {
34
- > header {
35
- margin-bottom: var(--pico-spacing);
36
- }
37
-
38
- header {
39
- display: flex;
40
- justify-content: space-between;
41
- align-items: center;
42
-
43
- background-color: inherit;
44
-
45
- h6,
46
- h5,
47
- fieldset {
48
- margin-bottom: 0;
49
- }
50
-
51
- button {
52
- padding: 0;
53
- background-color: white;
54
-
55
- svg {
56
- padding: 8px;
57
- }
58
- }
59
- }
60
- }
61
-
62
- [data-sortable] {
63
- > article {
64
- transition: opacity 200ms;
65
- }
66
-
67
- .sortable-ghost {
68
- opacity: 0.3;
69
- }
70
-
71
- /* .sortable-chosen {
72
- filter: brightness(0.9)
73
- } */
74
- }
75
-
76
- div.ink-mde {
77
- border: var(--pico-border-width) solid #2a3140;
78
- border-radius: var(--pico-border-radius);
79
- outline: 0;
80
- background-color: var(--pico-background-color);
81
- box-shadow: var(--pico-box-shadow);
82
- color: var(--pico-color);
83
- }
84
-
85
- .ink-mde-textarea {
86
- width: 100%;
87
- }
88
-
89
- .disclamer {
90
- display: flex;
91
- justify-content: center;
92
- }
package/public/main.js DELETED
@@ -1,43 +0,0 @@
1
- import Sortable from '../node_modules/sortablejs/modular/sortable.core.esm.js'
2
- import { ink, wrap } from 'https://esm.sh/ink-mde@0.34.0'
3
-
4
- barba.init({
5
- views: [
6
- {
7
- namespace: 'default',
8
- afterEnter() {
9
- init()
10
- },
11
- },
12
- ],
13
- })
14
-
15
- function init() {
16
- // sortable
17
- {
18
- const els = document.querySelectorAll('[data-sortable]')
19
-
20
- els.forEach((el) => {
21
- var sortable = Sortable.create(el, {
22
- delay: 0,
23
- animation: 250,
24
- dragClass: 'sortable-drag',
25
- easing: 'ease-in-out',
26
- })
27
- })
28
- }
29
-
30
- // ink-mde
31
- {
32
- const el = document.querySelector('textarea')
33
-
34
- if (el) {
35
- wrap(el, {
36
- interface: {
37
- // appearance: InkValues.Appearance.Auto,
38
- attribution: false,
39
- },
40
- })
41
- }
42
- }
43
- }
File without changes