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

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 (69) hide show
  1. package/api/api-key.ts +69 -0
  2. package/api/auth.ts +66 -0
  3. package/api/backup.ts +40 -0
  4. package/api/block.ts +131 -66
  5. package/api/index.ts +19 -1
  6. package/api/mcp.ts +50 -0
  7. package/components/AdminPanel.ts +87 -0
  8. package/components/Backup.ts +13 -0
  9. package/components/BlockFieldRenderer.ts +125 -0
  10. package/components/BlockRenderer.ts +22 -0
  11. package/components/Entries.ts +20 -12
  12. package/components/Entry.ts +13 -21
  13. package/components/FieldRenderer.ts +35 -0
  14. package/components/Render.ts +46 -0
  15. package/components/Settings.ts +104 -0
  16. package/components/SiteLayout.ts +61 -0
  17. package/components/Users.ts +46 -0
  18. package/components/fields/Markdown.ts +44 -0
  19. package/components/fields/Slug.ts +113 -0
  20. package/components/fields/Text.ts +42 -0
  21. package/components/fields/index.ts +7 -0
  22. package/components/icons.ts +136 -7
  23. package/index.ts +94 -34
  24. package/package.json +10 -7
  25. package/pages/entry/[id].ts +15 -0
  26. package/pages/error.ts +14 -0
  27. package/{components → pages}/index.ts +7 -4
  28. package/pages/login.ts +21 -0
  29. package/pages/register.ts +33 -0
  30. package/pages/settings.ts +8 -0
  31. package/public/studio/css/admin-panel.css +103 -0
  32. package/public/studio/css/blocks-field.css +53 -0
  33. package/public/studio/css/settings.css +28 -0
  34. package/public/studio/js/markdown-editor.js +34 -0
  35. package/public/studio/js/sortable-list.js +50 -0
  36. package/public/studio/main.css +166 -0
  37. package/public/studio/main.js +21 -0
  38. package/queries/block-2.ts +339 -0
  39. package/queries/block-with-children.ts +74 -0
  40. package/queries/block.ts +289 -0
  41. package/queries/db-types.ts +15 -0
  42. package/queries/getBlockTrees-2.ts +71 -0
  43. package/queries/getBlockTrees.ts +316 -0
  44. package/queries/getBlocks.ts +214 -0
  45. package/queries/index.ts +2 -98
  46. package/queries/structure-types.ts +97 -0
  47. package/readme.md +205 -0
  48. package/schema.sql +18 -0
  49. package/schemas.ts +23 -52
  50. package/types.ts +144 -5
  51. package/utils/auth.ts +54 -0
  52. package/utils/buildBlocksTree.ts +4 -4
  53. package/utils/create-hash.ts +9 -0
  54. package/utils/define.ts +39 -0
  55. package/utils/file-based-router.ts +11 -2
  56. package/utils/get-config.ts +8 -9
  57. package/utils/get-or-create-row.ts +41 -0
  58. package/utils/html.ts +247 -0
  59. package/utils/startup-log.ts +19 -0
  60. package/components/AdminPanel/AdminPanel.css +0 -59
  61. package/components/AdminPanel/AdminPanel.ts +0 -57
  62. package/components/Block.ts +0 -116
  63. package/components/Entry.css +0 -7
  64. package/components/Field.ts +0 -164
  65. package/components/Fields.ts +0 -43
  66. package/components/layout.ts +0 -53
  67. package/public/main.css +0 -92
  68. package/public/main.js +0 -43
  69. /package/public/{favicon.svg → studio/favicon.svg} +0 -0
@@ -0,0 +1,103 @@
1
+ .admin-panel {
2
+ /* background: hsla(0, 0%, 0%, 0.1); */
3
+ padding: 40px;
4
+ margin-bottom: 0;
5
+ display: flex;
6
+ flex-direction: column;
7
+ align-items: flex-start;
8
+
9
+ height: 100%;
10
+ min-height: inherit;
11
+
12
+ min-width: 250px;
13
+
14
+ > h1 {
15
+ padding-bottom: 1rem;
16
+
17
+ a {
18
+ display: flex;
19
+ }
20
+
21
+ svg {
22
+ height: 1.6rem;
23
+ }
24
+ }
25
+
26
+ form {
27
+ padding-bottom: 1rem;
28
+
29
+ button {
30
+ margin: 10px 0px 20px;
31
+ }
32
+
33
+ p {
34
+ margin: 0;
35
+
36
+ small {
37
+ text-transform: uppercase;
38
+ letter-spacing: 0.4px;
39
+ font-weight: 600;
40
+ opacity: 0.6;
41
+ }
42
+ }
43
+ }
44
+
45
+ #entries {
46
+ width: 100%;
47
+ user-select: none;
48
+
49
+ ul {
50
+ padding: 0;
51
+ margin-inline: -1rem;
52
+
53
+ form {
54
+ display: flex;
55
+ padding-bottom: 0;
56
+
57
+ button {
58
+ margin: 0;
59
+ }
60
+ }
61
+
62
+ > li {
63
+ margin-bottom: 0px;
64
+ border-radius: 8px;
65
+ display: flex;
66
+ justify-content: space-between;
67
+ align-items: stretch;
68
+ list-style: none;
69
+ padding: 0;
70
+
71
+ a {
72
+ text-decoration: none;
73
+ width: 100%;
74
+ padding: 0.5rem 1rem;
75
+ }
76
+
77
+ button {
78
+ border-radius: 7px;
79
+ opacity: 0;
80
+ transition: opacity 100px;
81
+
82
+ svg {
83
+ margin: 0.5rem 1rem;
84
+ }
85
+ }
86
+ }
87
+ }
88
+ }
89
+
90
+ > footer {
91
+ margin-top: auto;
92
+ }
93
+ }
94
+
95
+ #entries ul {
96
+ > li:hover {
97
+ background-color: var(--pico-form-element-background-color);
98
+
99
+ button {
100
+ opacity: 1;
101
+ }
102
+ }
103
+ }
@@ -0,0 +1,53 @@
1
+ .blocks-field {
2
+ > header {
3
+ display: flex;
4
+ justify-content: space-between;
5
+ align-items: center;
6
+
7
+ > form fieldset {
8
+ button {
9
+ padding-inline: 1rem;
10
+ }
11
+ }
12
+
13
+ details {
14
+ form {
15
+ display: flex;
16
+ }
17
+
18
+ button {
19
+ width: 100%;
20
+ margin-bottom: 0;
21
+ text-align: left;
22
+ }
23
+
24
+ summary + ul {
25
+ right: 0;
26
+ left: auto;
27
+ }
28
+ }
29
+ }
30
+
31
+ article {
32
+ > header {
33
+ display: flex;
34
+ justify-content: space-between;
35
+ align-items: center;
36
+
37
+ aside {
38
+ display: flex;
39
+ align-items: center;
40
+ gap: 5px;
41
+ }
42
+
43
+ button {
44
+ margin: 0;
45
+ padding: 0;
46
+
47
+ svg {
48
+ padding: 5px;
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,28 @@
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
+ }
25
+
26
+ .login-form {
27
+ width: 100%;
28
+ }
@@ -0,0 +1,34 @@
1
+ import { ink } from 'ink-mde'
2
+
3
+ class MarkdownEditor extends HTMLElement {
4
+ instance = null
5
+
6
+ connectedCallback() {
7
+ this.style.width = '100%'
8
+
9
+ this.instance = ink(this, {
10
+ hooks: {
11
+ afterUpdate: async (e) => {
12
+ await fetch('/studio/api/value', {
13
+ method: 'PATCH',
14
+ body: JSON.stringify({
15
+ value: e,
16
+ id: this.dataset.id,
17
+ }),
18
+ })
19
+ },
20
+ },
21
+ interface: {
22
+ attribution: false,
23
+ },
24
+ })
25
+
26
+ this.instance.update(this.dataset.content)
27
+ }
28
+
29
+ disconnectedCallback() {
30
+ // this.instance.destroy()
31
+ }
32
+ }
33
+
34
+ customElements.define('markdown-editor', MarkdownEditor)
@@ -0,0 +1,50 @@
1
+ import Sortable from 'sortablejs'
2
+
3
+ class SortableList extends HTMLElement {
4
+ instance = null
5
+
6
+ mutationObserver
7
+
8
+ init() {
9
+ if (this.instance || !this.children.length) return
10
+
11
+ const { id } = this.dataset
12
+
13
+ this.instance = Sortable.create(this, {
14
+ animation: 250,
15
+ handle: `[data-handle-for="${id}"]`,
16
+ onEnd: (e) => {
17
+ const items = [...e.target.children]
18
+
19
+ items.forEach(async (child, idx) => {
20
+ const searchParams = new URLSearchParams()
21
+
22
+ searchParams.set('id', child.dataset.id)
23
+ searchParams.set('sort-order', idx)
24
+
25
+ await fetch(`/studio/api/sort-order?${searchParams.toString()}`, {
26
+ method: 'post',
27
+ })
28
+ })
29
+
30
+ this.querySelectorAll('[name="sort_order"]').forEach((input, idx) => {
31
+ input.value = idx.toString()
32
+ })
33
+ },
34
+ })
35
+ }
36
+
37
+ connectedCallback() {
38
+ this.mutationObserver = new MutationObserver(this.init.bind(this))
39
+ this.mutationObserver.observe(this, { childList: true })
40
+
41
+ this.init()
42
+ }
43
+
44
+ disconnectedCallback() {
45
+ this.instance?.destroy()
46
+ this.mutationObserver?.disconnect()
47
+ }
48
+ }
49
+
50
+ customElements.define('sortable-list', SortableList)
@@ -0,0 +1,166 @@
1
+ /* @import './../node_modules/@alstar/ui/red.css'; */
2
+ @import 'https://esm.sh/@alstar/ui/red.css';
3
+ @import './css/admin-panel.css';
4
+ @import './css/blocks-field.css';
5
+ @import './css/settings.css';
6
+
7
+ body {
8
+ padding: 0;
9
+ min-height: 100vh;
10
+
11
+ display: grid;
12
+ grid-template-columns: auto 1fr;
13
+ align-content: baseline;
14
+
15
+ > main {
16
+ padding: 40px;
17
+ height: 100vh;
18
+ overflow: auto;
19
+ width: 100%;
20
+
21
+ section {
22
+ max-width: 900px;
23
+ margin: 0 auto;
24
+ }
25
+ }
26
+ }
27
+
28
+ button svg, a svg {
29
+ pointer-events: none;
30
+ }
31
+
32
+ .entry > .fields > .block {
33
+ padding-block: var(--pico-spacing);
34
+
35
+ > header {
36
+ margin-block: var(--pico-block-spacing-vertical);
37
+ }
38
+ }
39
+
40
+ .block {
41
+ > header {
42
+ margin-bottom: var(--pico-spacing);
43
+ }
44
+
45
+ header {
46
+ display: flex;
47
+ justify-content: space-between;
48
+ align-items: center;
49
+
50
+ background-color: inherit;
51
+
52
+ h6,
53
+ h5,
54
+ fieldset {
55
+ margin-bottom: 0;
56
+ }
57
+
58
+ button {
59
+ padding: 0;
60
+ background-color: white;
61
+
62
+ svg {
63
+ padding: 8px;
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ [data-sortable] {
70
+ > article {
71
+ transition: opacity 200ms;
72
+ }
73
+
74
+ .sortable-ghost {
75
+ opacity: 0.3;
76
+ }
77
+ }
78
+
79
+ div.ink-mde {
80
+ border: var(--pico-border-width) solid #2a3140;
81
+ border-radius: var(--pico-border-radius);
82
+ outline: 0;
83
+ background-color: var(--pico-background-color);
84
+ box-shadow: var(--pico-box-shadow);
85
+ color: var(--pico-color);
86
+ }
87
+
88
+ .ͼ1 .cm-content.ink-mde-editor-content {
89
+ min-height: 10lh;
90
+ }
91
+
92
+ div.ink-mde {
93
+ --pico-border-color: var(--pico-form-element-border-color);
94
+
95
+ border: var(--pico-border-width) solid var(--pico-border-color);
96
+ border-radius: var(--pico-border-radius);
97
+
98
+ padding: var(--pico-form-element-spacing-vertical)
99
+ var(--pico-form-element-spacing-horizontal);
100
+ margin-bottom: var(--pico-spacing);
101
+
102
+ --pico-background-color: var(--pico-form-element-background-color);
103
+
104
+ --pico-color: var(--pico-form-element-color);
105
+ --pico-box-shadow: none;
106
+
107
+ outline: 0;
108
+ background-color: var(--pico-background-color);
109
+ box-shadow: var(--pico-box-shadow);
110
+ color: var(--pico-color);
111
+ font-weight: var(--pico-font-weight);
112
+ transition:
113
+ background-color var(--pico-transition),
114
+ border-color var(--pico-transition),
115
+ color var(--pico-transition),
116
+ box-shadow var(--pico-transition);
117
+ }
118
+
119
+ .ink-mde-textarea {
120
+ }
121
+
122
+ div.ink-mde:has(.cm-focused) {
123
+ --pico-outline-width: 0.1rem;
124
+ --pico-box-shadow: 0 0 0 var(--pico-outline-width)
125
+ var(--pico-form-element-focus-color);
126
+
127
+ box-shadow: var(--pico-box-shadow);
128
+ }
129
+
130
+ .ͼ1.cm-focused {
131
+ outline: none;
132
+ }
133
+
134
+ .ink-mde-textarea {
135
+ width: 100%;
136
+
137
+ --pico-background-color: var(--pico-form-element-background-color);
138
+
139
+ --pico-color: var(--pico-form-element-color);
140
+ --pico-box-shadow: none;
141
+
142
+ outline: 0;
143
+ background-color: var(--pico-background-color);
144
+ box-shadow: var(--pico-box-shadow);
145
+ color: var(--pico-color);
146
+ font-weight: var(--pico-font-weight);
147
+ transition:
148
+ background-color var(--pico-transition),
149
+ border-color var(--pico-transition),
150
+ color var(--pico-transition),
151
+ box-shadow var(--pico-transition);
152
+ }
153
+
154
+ .disclamer {
155
+ display: flex;
156
+ justify-content: center;
157
+ }
158
+
159
+ .text-secondary {
160
+ color: var(--pico-secondary);
161
+ }
162
+
163
+ .markdown {
164
+ width: 100%;
165
+ height: 10lh;
166
+ }
@@ -0,0 +1,21 @@
1
+ import barba from '@barba/core'
2
+
3
+ import './js/markdown-editor.js'
4
+ import './js/sortable-list.js'
5
+
6
+ barba.init({
7
+ cacheIgnore: true,
8
+ views: [{ namespace: 'default' }],
9
+ })
10
+
11
+ // client side script
12
+ // const eventSource = new EventSource('http://localhost:5432')
13
+ // eventSource.onmessage = ({ data }) => {
14
+ // const delay = data.split(' - ').at(-1) === 'true'
15
+ // setTimeout(() => window.location.reload(), delay ? 0 : 1000)
16
+ // }
17
+
18
+ // console.log(
19
+ // '%c REFRESHER ACTIVE ',
20
+ // 'color: green; background: lightgreen; border-radius: 2px'
21
+ // )