@fullstackunicorn/create-root 1.0.27 → 1.0.29
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/assets/root.fullstackunicorn.dev/assets/config.cmd.sh +2 -2
- package/assets/root.fullstackunicorn.dev/backend/modules/loader.js +1 -0
- package/assets/root.fullstackunicorn.dev/backend/modules/pulse.js +22 -0
- package/assets/root.fullstackunicorn.dev/backend/server/router.js +10 -0
- package/assets/root.fullstackunicorn.dev/frontend/App.uri.js +10 -4
- package/assets/root.fullstackunicorn.dev/frontend/assets/index.webmanifest +3 -3
- package/assets/root.fullstackunicorn.dev/frontend/base/account/@use.js +9 -16
- package/assets/root.fullstackunicorn.dev/frontend/base/alerts/Alert.pod.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/base/callout/Callout.pod.js +3 -3
- package/assets/root.fullstackunicorn.dev/frontend/base/cookies/@tools.js +2 -46
- package/assets/root.fullstackunicorn.dev/frontend/base/layout/Header.pod.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/base/layout/Layout.pod.js +3 -3
- package/assets/root.fullstackunicorn.dev/frontend/base/layout/sideTwo/IconTwo.pod.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/base/layout/sideTwo/SidebarTwo.pod.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/base/loader/@tools.js +2 -1
- package/assets/root.fullstackunicorn.dev/frontend/base/modals/Modal.pod.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/base/modals/ModalPopup.pod.js +3 -3
- package/assets/root.fullstackunicorn.dev/frontend/client/auth.js +52 -66
- package/assets/root.fullstackunicorn.dev/frontend/client/statics.js +3 -2
- package/assets/root.fullstackunicorn.dev/frontend/configure/env.json +1 -0
- package/assets/root.fullstackunicorn.dev/frontend/configure/env.prod.json +1 -0
- package/assets/root.fullstackunicorn.dev/frontend/imports/animated/Button.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/imports/animated/Crossfader.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/imports/animated/HoverCard.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/imports/client.js +22 -10
- package/assets/root.fullstackunicorn.dev/frontend/imports/core/Img.js +62 -10
- package/assets/root.fullstackunicorn.dev/frontend/imports/core/Img.pod.js +4 -4
- package/assets/root.fullstackunicorn.dev/frontend/imports/core/Scrollable.js +10 -1
- package/assets/root.fullstackunicorn.dev/frontend/imports/core/Text.js +11 -2
- package/assets/root.fullstackunicorn.dev/frontend/imports/device.js +46 -0
- package/assets/root.fullstackunicorn.dev/frontend/imports/router.js +2 -0
- package/assets/root.fullstackunicorn.dev/frontend/imports/service.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/index.html +5 -5
- package/assets/root.fullstackunicorn.dev/frontend/modules/base/ComingSoon.pod.js +2 -2
- package/assets/root.fullstackunicorn.dev/frontend/modules/base/PageTitle.pod.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/modules/layout/Wrapper.pod.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/modules/modals/select/SelectItem.pod.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/modules/modals/select/SelectPicked.pod.js +1 -1
- package/assets/root.fullstackunicorn.dev/frontend/panels/slideshows/Slideshow.pod.js +4 -4
- package/assets/root.fullstackunicorn.dev/frontend/templates/cards.js +9 -9
- package/assets/root.fullstackunicorn.dev/frontend/templates/page.js +4 -4
- package/assets/root.fullstackunicorn.dev/frontend/templates/root.js +1 -1
- package/assets/root.fullstackunicorn.dev/manager/modules/pulse.js +22 -0
- package/assets/root.fullstackunicorn.dev/manager/server/router.js +11 -1
- package/assets/root.fullstackunicorn.dev/workspace/config/workers.json +14 -2
- package/index.cmd.help.js +33 -0
- package/index.cmd.js +20 -3
- package/package.json +2 -1
|
@@ -109,7 +109,7 @@ class Client {
|
|
|
109
109
|
if (isFresh) return cachedResponse
|
|
110
110
|
|
|
111
111
|
config.timeout = 10000
|
|
112
|
-
if (etag) {
|
|
112
|
+
if (etag && cachedItem) {
|
|
113
113
|
if (!config.headers) config.headers = {}
|
|
114
114
|
config.headers['If-None-Match'] = etag
|
|
115
115
|
}
|
|
@@ -121,6 +121,7 @@ class Client {
|
|
|
121
121
|
: await axios[method](endpoint, data, config)
|
|
122
122
|
|
|
123
123
|
if (cachedItem && response.status === 304) {
|
|
124
|
+
await cache.setMeta(key, response.headers['etag'] || etag)
|
|
124
125
|
return cachedResponse
|
|
125
126
|
}
|
|
126
127
|
|
|
@@ -139,21 +140,25 @@ class Client {
|
|
|
139
140
|
|
|
140
141
|
const cacheStore = await cache.open(this.cacheNameImg)
|
|
141
142
|
const cachedBlob = await cache.get(cacheStore, key, true)
|
|
142
|
-
if (!cachedBlob) return null
|
|
143
|
-
return { key, blob: cachedBlob }
|
|
143
|
+
if (!cachedBlob?.size) return null
|
|
144
|
+
return { key, blob: cachedBlob, isCached: true }
|
|
144
145
|
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
cacheImgSet = async ({ key, endpoint, config = {} }) => {
|
|
148
149
|
const cacheStore = await cache.open(this.cacheNameImg)
|
|
149
150
|
const cachedBlob = await cache.get(cacheStore, key, true)
|
|
150
|
-
const cachedResponse = cachedBlob ? { key, blob: cachedBlob } : undefined
|
|
151
|
+
const cachedResponse = cachedBlob?.size ? { key, blob: cachedBlob, isCached: true } : undefined
|
|
151
152
|
const { etag, timestamp } = await cache.getMeta(key)
|
|
152
153
|
|
|
154
|
+
const age = (Date.now() - timestamp) / 1000
|
|
155
|
+
const isFresh = cachedResponse && age < this.cacheStale
|
|
156
|
+
if (isFresh) return { ...cachedResponse, isFresh: true }
|
|
157
|
+
|
|
153
158
|
config.responseType = 'arraybuffer'
|
|
154
159
|
config.timeout = 10000
|
|
155
160
|
|
|
156
|
-
if (etag) {
|
|
161
|
+
if (etag && cachedResponse) {
|
|
157
162
|
if (!config.headers) config.headers = {}
|
|
158
163
|
config.headers['If-None-Match'] = etag
|
|
159
164
|
}
|
|
@@ -161,15 +166,19 @@ class Client {
|
|
|
161
166
|
try {
|
|
162
167
|
const response = await axios.get(endpoint, config)
|
|
163
168
|
|
|
164
|
-
if (
|
|
169
|
+
if (cachedResponse && response.status === 304) {
|
|
170
|
+
await cache.setMeta(key, response.headers['etag'] || etag)
|
|
171
|
+
return { ...cachedResponse, isRevalidated: true }
|
|
172
|
+
}
|
|
165
173
|
|
|
166
174
|
const blob = cache.getBlob(response.data, response.headers['content-type'])
|
|
167
175
|
await cache.set(cacheStore, key, blob, true)
|
|
168
176
|
await cache.setMeta(key, response.headers['etag'])
|
|
169
177
|
|
|
170
|
-
return { key, blob }
|
|
178
|
+
return { key, blob, isUpdated: true }
|
|
171
179
|
|
|
172
180
|
} catch (error) {
|
|
181
|
+
if (error?.code === 'ERR_CANCELED' || config.signal?.aborted) return cachedResponse || { isCanceled: true }
|
|
173
182
|
return await err.cache(error, cachedResponse)
|
|
174
183
|
}
|
|
175
184
|
}
|
|
@@ -178,7 +187,7 @@ class Client {
|
|
|
178
187
|
|
|
179
188
|
const cacheStore = await cache.open(this.cacheNameFiles)
|
|
180
189
|
const cachedBlob = await cache.get(cacheStore, key, true)
|
|
181
|
-
const cachedResponse = cachedBlob ? { key, blob: cachedBlob } : undefined
|
|
190
|
+
const cachedResponse = cachedBlob?.size ? { key, blob: cachedBlob } : undefined
|
|
182
191
|
const { etag, timestamp } = await cache.getMeta(key)
|
|
183
192
|
|
|
184
193
|
const age = (Date.now() - timestamp) / 1000
|
|
@@ -188,7 +197,7 @@ class Client {
|
|
|
188
197
|
config.responseType = 'arraybuffer'
|
|
189
198
|
config.timeout = 10000
|
|
190
199
|
|
|
191
|
-
if (etag) {
|
|
200
|
+
if (etag && cachedResponse) {
|
|
192
201
|
if (!config.headers) config.headers = {}
|
|
193
202
|
config.headers['If-None-Match'] = etag
|
|
194
203
|
}
|
|
@@ -198,7 +207,10 @@ class Client {
|
|
|
198
207
|
? await axios.get(endpoint, config)
|
|
199
208
|
: await axios[method](endpoint, data, config)
|
|
200
209
|
|
|
201
|
-
if (
|
|
210
|
+
if (cachedResponse && response.status === 304) {
|
|
211
|
+
await cache.setMeta(key, response.headers['etag'] || etag)
|
|
212
|
+
return cachedResponse
|
|
213
|
+
}
|
|
202
214
|
|
|
203
215
|
const blob = cache.getBlob(response.data, response.headers['content-type'])
|
|
204
216
|
await cache.set(cacheStore, key, blob, true)
|
|
@@ -6,6 +6,34 @@ import { api } from '#@/client/@api.js'
|
|
|
6
6
|
|
|
7
7
|
export const ImgData = (props) => <ImgDataPod {...props} />
|
|
8
8
|
|
|
9
|
+
const inFlightImages = new Map()
|
|
10
|
+
|
|
11
|
+
const retainImageRequest = (src) => {
|
|
12
|
+
let entry = inFlightImages.get(src)
|
|
13
|
+
|
|
14
|
+
if (!entry) {
|
|
15
|
+
const controller = new AbortController()
|
|
16
|
+
const promise = api.statics.image(src, 'set', { signal: controller.signal })
|
|
17
|
+
.finally(() => inFlightImages.delete(src))
|
|
18
|
+
|
|
19
|
+
entry = { controller, promise, retainers: 0 }
|
|
20
|
+
inFlightImages.set(src, entry)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let released = false
|
|
24
|
+
entry.retainers += 1
|
|
25
|
+
|
|
26
|
+
const release = () => {
|
|
27
|
+
if (released) return
|
|
28
|
+
released = true
|
|
29
|
+
|
|
30
|
+
entry.retainers = Math.max(0, entry.retainers - 1)
|
|
31
|
+
if (entry.retainers === 0) entry.controller.abort()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { promise: entry.promise, release }
|
|
35
|
+
}
|
|
36
|
+
|
|
9
37
|
export const NoImg = (props) => {
|
|
10
38
|
const statics = useGetStatics()
|
|
11
39
|
return <NoImgPod {...props} svg={statics.visual.svg.picture} />
|
|
@@ -19,9 +47,12 @@ export const Img = ({ classes, inline, picture = {} }) => {
|
|
|
19
47
|
const [isSlow, setIsSlow] = useState(false)
|
|
20
48
|
|
|
21
49
|
useEffect(() => {
|
|
50
|
+
if (image.isNoImage || !image.src) return
|
|
51
|
+
|
|
22
52
|
let mounted = true
|
|
23
53
|
let objectUrl = null
|
|
24
54
|
let slowTimer = null
|
|
55
|
+
let releaseRequest = null
|
|
25
56
|
|
|
26
57
|
setSrc(undefined)
|
|
27
58
|
setIsLoaded(false)
|
|
@@ -31,25 +62,45 @@ export const Img = ({ classes, inline, picture = {} }) => {
|
|
|
31
62
|
if (mounted) setIsSlow(true)
|
|
32
63
|
}, 150)
|
|
33
64
|
|
|
65
|
+
const setBlob = (blob) => {
|
|
66
|
+
clearTimeout(slowTimer)
|
|
67
|
+
if (objectUrl) URL.revokeObjectURL(objectUrl)
|
|
68
|
+
objectUrl = URL.createObjectURL(blob)
|
|
69
|
+
setSrc(objectUrl)
|
|
70
|
+
setIsLoaded(true)
|
|
71
|
+
setIsSlow(false)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const fetchImage = async ({ updateOnly = false } = {}) => {
|
|
75
|
+
const request = retainImageRequest(image.src)
|
|
76
|
+
releaseRequest = request.release
|
|
77
|
+
|
|
78
|
+
const fetched = await request.promise
|
|
79
|
+
releaseRequest()
|
|
80
|
+
releaseRequest = null
|
|
81
|
+
|
|
82
|
+
if (fetched?.isCanceled) return fetched
|
|
83
|
+
if (!mounted) return fetched
|
|
84
|
+
if (!fetched?.blob) return fetched
|
|
85
|
+
if (updateOnly && !fetched.isUpdated) return fetched
|
|
86
|
+
|
|
87
|
+
setBlob(fetched.blob)
|
|
88
|
+
return fetched
|
|
89
|
+
}
|
|
90
|
+
|
|
34
91
|
const load = async () => {
|
|
35
92
|
const cached = await api.statics.image(image.src, 'get')
|
|
36
93
|
|
|
37
94
|
if (mounted && cached?.blob) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
setSrc(objectUrl)
|
|
41
|
-
setIsLoaded(true)
|
|
42
|
-
setIsSlow(false)
|
|
95
|
+
setBlob(cached.blob)
|
|
96
|
+
fetchImage({ updateOnly: true })
|
|
43
97
|
return
|
|
44
98
|
}
|
|
45
99
|
|
|
46
|
-
const fetched = await
|
|
100
|
+
const fetched = await fetchImage()
|
|
101
|
+
if (fetched?.isCanceled) return
|
|
47
102
|
|
|
48
103
|
if (mounted && fetched?.blob) {
|
|
49
|
-
clearTimeout(slowTimer)
|
|
50
|
-
objectUrl = URL.createObjectURL(fetched.blob)
|
|
51
|
-
setSrc(objectUrl)
|
|
52
|
-
setIsLoaded(true)
|
|
53
104
|
return
|
|
54
105
|
}
|
|
55
106
|
|
|
@@ -68,6 +119,7 @@ export const Img = ({ classes, inline, picture = {} }) => {
|
|
|
68
119
|
return () => {
|
|
69
120
|
mounted = false
|
|
70
121
|
clearTimeout(slowTimer)
|
|
122
|
+
if (releaseRequest) releaseRequest()
|
|
71
123
|
if (objectUrl) URL.revokeObjectURL(objectUrl)
|
|
72
124
|
}
|
|
73
125
|
}, [image.src])
|
|
@@ -17,7 +17,7 @@ export const NoImgPod = ({ classes, inline, svg }) => {
|
|
|
17
17
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
18
18
|
|
|
19
19
|
S.box = [...classes, c.base_NN_W700]
|
|
20
|
-
S.svg = [s.
|
|
20
|
+
S.svg = [s.XY100p, c.item_NN_W750]
|
|
21
21
|
|
|
22
22
|
return (
|
|
23
23
|
<View classes={S.box} inline={inline}>
|
|
@@ -37,9 +37,9 @@ export const ImgPod = ({
|
|
|
37
37
|
B.imgTransition = isSlow ? { transition: '0.3s all ease-in-out', ...inline } : { ...inline }
|
|
38
38
|
B.blurhashTransition = { transition: '0.3s all ease-in-out' }
|
|
39
39
|
|
|
40
|
-
S.image = [s.
|
|
41
|
-
S.blurhash = [s.
|
|
42
|
-
S.box = [s.
|
|
40
|
+
S.image = [s.positionRelative, s.overflowHidden, s.row_cXcY, ...classes]
|
|
41
|
+
S.blurhash = [s.positionAbsolute, s.XY100p, ...B.blurhashOpacity]
|
|
42
|
+
S.box = [s.positionAbsolute, s.XY100p, s.row_cXcY]
|
|
43
43
|
S.img = [...B.imgOpacity, ...classes]
|
|
44
44
|
|
|
45
45
|
I.image = { objectFit: 'cover', aspectRatio: ratio, ...inline }
|
|
@@ -7,9 +7,18 @@ export const Scrollable = ({
|
|
|
7
7
|
scrollRef,
|
|
8
8
|
onScroll,
|
|
9
9
|
isPage,
|
|
10
|
+
x,
|
|
11
|
+
y,
|
|
12
|
+
xy,
|
|
10
13
|
}) => {
|
|
11
14
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
12
15
|
|
|
16
|
+
B.axis = xy || (x && y) ? 'xy' : x ? 'x' : 'y'
|
|
17
|
+
B.isXY = B.axis === 'xy' ? [s.overflowScroll] : []
|
|
18
|
+
B.isX = B.axis === 'x' ? [s.overflowXScroll, s.overflowYHidden] : []
|
|
19
|
+
B.isY = B.axis === 'y' ? [s.overflowYScroll] : []
|
|
20
|
+
B.scroll = [...B.isXY, ...B.isX, ...B.isY]
|
|
21
|
+
|
|
13
22
|
const pageInline = isPage
|
|
14
23
|
? { paddingBottom: 'env(safe-area-inset-bottom, 0px)' }
|
|
15
24
|
: {}
|
|
@@ -22,7 +31,7 @@ export const Scrollable = ({
|
|
|
22
31
|
return (
|
|
23
32
|
<div
|
|
24
33
|
ref={scrollRef}
|
|
25
|
-
className={[...classes,
|
|
34
|
+
className={[...classes, ...B.scroll].join(' ')}
|
|
26
35
|
style={{ ...scrollInline, ...inline, ...pageInline }}
|
|
27
36
|
onScroll={onScroll}
|
|
28
37
|
>
|
|
@@ -37,9 +37,14 @@ export const Text = (props = {}) => {
|
|
|
37
37
|
|
|
38
38
|
F.font = useGetFont(props.font, props.language)
|
|
39
39
|
B.select = props.isNoSelect ? [s.noSelect] : []
|
|
40
|
+
B.mode = props.ellipsis || props.oneLine
|
|
41
|
+
? [s.textline]
|
|
42
|
+
: props.wrap || props.preserveWhitespace
|
|
43
|
+
? [s.textwrap]
|
|
44
|
+
: [s.textblock]
|
|
40
45
|
|
|
41
46
|
const next = { ...props }
|
|
42
|
-
next.classes = [...classes, ...B.select,
|
|
47
|
+
next.classes = [...classes, ...B.select, ...B.mode, F.font]
|
|
43
48
|
|
|
44
49
|
if (props.tag) return <TextTag {...next} />
|
|
45
50
|
else return <TextSpan {...next} />
|
|
@@ -62,6 +67,10 @@ export const Textline = (props = {}) => {
|
|
|
62
67
|
else return <TextSpan {...next} />
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
export const TextEllipsis = (props = {}) => {
|
|
71
|
+
return <Textline {...props} ellipsis />
|
|
72
|
+
}
|
|
73
|
+
|
|
65
74
|
// use it for inline code where u need to pass the style to a span
|
|
66
75
|
export const TextFont = (props = {}) => {
|
|
67
76
|
|
|
@@ -85,7 +94,7 @@ export const TextCodeView = ({
|
|
|
85
94
|
F.font = useGetFont(f.m1)
|
|
86
95
|
|
|
87
96
|
B.isInline = type === 'inline' ? [s.textblock] : []
|
|
88
|
-
B.isBlock = type === 'block' ? [s.textwrap, s.
|
|
97
|
+
B.isBlock = type === 'block' ? [s.textwrap, s.overflowYScroll] : []
|
|
89
98
|
B.isView = type === 'view' ? [s.textwrap] : []
|
|
90
99
|
|
|
91
100
|
S.classes = [...classes, F.font, ...B.isInline, ...B.isBlock, ...B.isView].join(' ')
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const getSubmitMetadata = async ({ clientId, deviceId }) => {
|
|
2
|
+
const battery = navigator.getBattery ? await navigator.getBattery() : {}
|
|
3
|
+
const metadataObj = {
|
|
4
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
5
|
+
language: window.navigator.language,
|
|
6
|
+
pageTitle: document.title,
|
|
7
|
+
referrer: document.referrer,
|
|
8
|
+
pageURL: window.location.href,
|
|
9
|
+
platform: window.navigator.platform,
|
|
10
|
+
deviceMemory: navigator.deviceMemory,
|
|
11
|
+
screenWidth: window.screen.width,
|
|
12
|
+
screenHeight: window.screen.height,
|
|
13
|
+
colorDepth: window.screen.colorDepth,
|
|
14
|
+
maxScreenWidth: window.screen.width * window.devicePixelRatio,
|
|
15
|
+
maxScreenHeight: window.screen.height * window.devicePixelRatio,
|
|
16
|
+
pixelRatio: window.devicePixelRatio,
|
|
17
|
+
screenOrientation: screen?.orientation?.type,
|
|
18
|
+
hardwareConcurrency: navigator.hardwareConcurrency,
|
|
19
|
+
touchSupport: 'ontouchstart' in window || navigator.maxTouchPoints > 0,
|
|
20
|
+
colorGamut: window.matchMedia("(color-gamut: srgb)").matches
|
|
21
|
+
? "srgb"
|
|
22
|
+
: window.matchMedia("(color-gamut: p3)").matches
|
|
23
|
+
? "p3"
|
|
24
|
+
: window.matchMedia("(color-gamut: rec2020)").matches
|
|
25
|
+
? "rec2020"
|
|
26
|
+
: "unknown",
|
|
27
|
+
rtt: navigator?.connection?.rtt,
|
|
28
|
+
hasGamepad: navigator.getGamepads ? navigator.getGamepads().length > 0 : false,
|
|
29
|
+
keyboardEnabled: "onkeydown" in document,
|
|
30
|
+
cookieEnabled: navigator.cookieEnabled,
|
|
31
|
+
javaEnabled: navigator.javaEnabled ? navigator.javaEnabled() : false,
|
|
32
|
+
doNotTrack: navigator.doNotTrack,
|
|
33
|
+
networkType: navigator?.connection?.effectiveType,
|
|
34
|
+
downlinkSpeed: navigator?.connection?.downlink,
|
|
35
|
+
batteryLevel: battery?.level * 100,
|
|
36
|
+
batteryCharging: battery?.charging
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
submit_timestamp: new Date().toISOString(),
|
|
41
|
+
submit_device_id: deviceId,
|
|
42
|
+
submit_client_id: clientId,
|
|
43
|
+
submit_user_agent: window.navigator.userAgent,
|
|
44
|
+
submit_metadata: Object.entries(metadataObj).map(([key, value]) => `${key}:${value ? value : 'unknown'}`).join(" | ")
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -20,8 +20,8 @@ class Service {
|
|
|
20
20
|
const response = await store.match(key)
|
|
21
21
|
if (!response) return true
|
|
22
22
|
const lastDay = (await response.text()).trim()
|
|
23
|
-
return
|
|
23
|
+
return lastDay < this.todayUTC()
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export const service = new Service()
|
|
27
|
+
export const service = new Service()
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
<meta http-equiv="Expires" content="0">
|
|
13
13
|
|
|
14
14
|
<meta content="#ffffff" name="msapplication-TileColor" />
|
|
15
|
-
<meta content="/icons/icon-512.png" name="msapplication-TileImage" />
|
|
15
|
+
<meta content="/get/media/icons/icon-512.png" name="msapplication-TileImage" />
|
|
16
16
|
<meta content="Carlo" name="apple-mobile-web-app-title" />
|
|
17
|
-
<link href="/icons/icon-180.png" rel="apple-touch-icon" sizes="180x180" />
|
|
17
|
+
<link href="/get/media/icons/icon-180.png" rel="apple-touch-icon" sizes="180x180" />
|
|
18
18
|
<link href="/index.webmanifest" rel="manifest" />
|
|
19
19
|
|
|
20
|
-
<link href="/favicon.ico" rel="shortcut icon" />
|
|
21
|
-
<link href="/icons/favicon.png" rel="icon" type="image/png" />
|
|
20
|
+
<link href="/get/media/icons/favicon.ico" rel="shortcut icon" />
|
|
21
|
+
<link href="/get/media/icons/favicon.png" rel="icon" type="image/png" />
|
|
22
22
|
|
|
23
23
|
<!-- {{metadata}} -->
|
|
24
24
|
<!-- {{languages}} -->
|
|
@@ -33,4 +33,4 @@
|
|
|
33
33
|
<script type="module" src="/App.js"></script>
|
|
34
34
|
</body>
|
|
35
35
|
|
|
36
|
-
</html>
|
|
36
|
+
</html>
|
|
@@ -6,8 +6,8 @@ export const ComingSoonPod = ({ picture }) => {
|
|
|
6
6
|
|
|
7
7
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
8
8
|
|
|
9
|
-
S.box = [s.col_cXsY, s.
|
|
10
|
-
S.image = [s.
|
|
9
|
+
S.box = [s.col_cXsY, s.XY100p, s.marginB8]
|
|
10
|
+
S.image = [s.X100p, s.radiusB16]
|
|
11
11
|
|
|
12
12
|
return (
|
|
13
13
|
<View classes={S.box}>
|
|
@@ -14,7 +14,7 @@ export const SelectItemPod = ({ onPress=()=>{}, slug, value, img, isHover, setIs
|
|
|
14
14
|
|
|
15
15
|
S.item = [s.row_sXcY, s.Y48, s.radius8, s.marginB4, ...B.background, ...B.img]
|
|
16
16
|
S.imgBox = [s.X26, s.Y20, s.marginX7, s.marginY18, s.radius5, s.border1, c.line_NN, c.item_NN_B400]
|
|
17
|
-
S.img = [s.X24, s.Y18, s.
|
|
17
|
+
S.img = [s.X24, s.Y18, s.overflowHidden, s.radius4]
|
|
18
18
|
S.text = [...F.text, s.row_sXcY, s.paddingE12, c.item_NN_B400]
|
|
19
19
|
|
|
20
20
|
return (
|
|
@@ -17,7 +17,7 @@ export const SelectPickedPod = ({ value, slug, img, isHover, setIsHover=()=>{},
|
|
|
17
17
|
|
|
18
18
|
S.item = [s.row_sXcY, s.Y40, s.radius8, s.Xmin80, s.border1, c.line_NN_W500, ...B.hover, ...B.img]
|
|
19
19
|
S.imgBox = [s.X26, s.Y20, s.marginX7, s.radius5, s.border1, c.line_NN, c.item_NN_B400]
|
|
20
|
-
S.img = [s.X24, s.Y18, s.
|
|
20
|
+
S.img = [s.X24, s.Y18, s.overflowHidden, s.radius4]
|
|
21
21
|
S.textBox = [s.row_cY]
|
|
22
22
|
S.text = [...F.text, s.Xr, c.item_NN_B400]
|
|
23
23
|
S.icon = [s.XY40, B.icon]
|
|
@@ -11,8 +11,8 @@ export const SlideshowPod = ({ svg, onNext, onPrevious, aspectRatio, children })
|
|
|
11
11
|
|
|
12
12
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
13
13
|
|
|
14
|
-
S.slider = [s.row_cXcY, s.
|
|
15
|
-
S.controls = [s.row, s.
|
|
14
|
+
S.slider = [s.row_cXcY, s.X100p, s.positionRelative]
|
|
15
|
+
S.controls = [s.row, s.XY100p, s.Z3]
|
|
16
16
|
S.one = [s.row_sXcY, s.XYr]
|
|
17
17
|
S.iconOneBox = [s.XY64, s.col_cXcY, s.Z2]
|
|
18
18
|
S.iconOne = [s.XY40, s.Z2, s.marginX8, c.item_NN_B400, s.radius8, c.base_WW_50]
|
|
@@ -46,7 +46,7 @@ export const SlideshowTextualPod = ({ subhead, title, description, href, fontSiz
|
|
|
46
46
|
|
|
47
47
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
48
48
|
|
|
49
|
-
S.box = [s.row_sXcY, s.
|
|
49
|
+
S.box = [s.row_sXcY, s.positionAbsolute, s.XY100p, s.top0]
|
|
50
50
|
S.textual = [s.col_sXsY]
|
|
51
51
|
S.outline = [s.paddingX4, s.paddingY2, s.margin4, c.item_WW]
|
|
52
52
|
|
|
@@ -76,7 +76,7 @@ export const SlideshowItemPod = ({ isActive, picture, children, aspectRatio, tra
|
|
|
76
76
|
|
|
77
77
|
const { s, c, f, t, F, B, S, I, H } = useStyle()
|
|
78
78
|
|
|
79
|
-
S.image = [s.
|
|
79
|
+
S.image = [s.X100p]
|
|
80
80
|
I.image = { aspectRatio }
|
|
81
81
|
|
|
82
82
|
return (
|
|
@@ -5,18 +5,18 @@ export const getCards = (s, c, t) => {
|
|
|
5
5
|
|
|
6
6
|
cards.squared = {}
|
|
7
7
|
cards.squared.link = []
|
|
8
|
-
cards.squared.box = [s.
|
|
9
|
-
cards.squared.pictureWrap = [s.
|
|
10
|
-
cards.squared.pictureBox = [s.
|
|
11
|
-
cards.squared.picture = [s.
|
|
8
|
+
cards.squared.box = [s.X100p, s.radius24, s.overflowHidden, c.base_WW]
|
|
9
|
+
cards.squared.pictureWrap = [s.X100p, s.padding8]
|
|
10
|
+
cards.squared.pictureBox = [s.X100p, s.ratio, s.overflowHidden, s.radius16]
|
|
11
|
+
cards.squared.picture = [s.objectCover]
|
|
12
12
|
cards.squared.content = [s.paddingX16, s.paddingY16, s.col]
|
|
13
13
|
|
|
14
14
|
cards.portrait = {}
|
|
15
|
-
cards.portrait.link = [s.
|
|
16
|
-
cards.portrait.box = [s.
|
|
17
|
-
cards.portrait.pictureWrap = [s.
|
|
18
|
-
cards.portrait.pictureBox = [s.
|
|
19
|
-
cards.portrait.picture = [s.
|
|
15
|
+
cards.portrait.link = [s.Y100p]
|
|
16
|
+
cards.portrait.box = [s.X100p, s.Y100p, s.col, s.padding24, s.radius24, s.overflowHidden, s.border1, c.base_WW, c.line_NN_W600]
|
|
17
|
+
cards.portrait.pictureWrap = [s.X100p]
|
|
18
|
+
cards.portrait.pictureBox = [s.X100p, s.overflowHidden, s.radius16]
|
|
19
|
+
cards.portrait.picture = [s.X100p]
|
|
20
20
|
cards.portrait.content = [s.paddingT16, s.col, s.Yc]
|
|
21
21
|
cards.portrait.title = []
|
|
22
22
|
cards.portrait.price = [s.marginT16]
|
|
@@ -6,10 +6,10 @@ export const getPage = (s, c, t) => {
|
|
|
6
6
|
|
|
7
7
|
page.home = [s.col, s.XYc]
|
|
8
8
|
page.fluid = [s.col, s.XYc]
|
|
9
|
-
page.fixed = [s.col_cXsY, s.Yc, s.
|
|
10
|
-
page.fixedL = [s.col_cXsY, s.Yc, s.
|
|
11
|
-
page.fixedXL = [s.col_cXsY, s.Yc, s.
|
|
12
|
-
page.docs = [s.col_cXsY, s.Yc, s.
|
|
9
|
+
page.fixed = [s.col_cXsY, s.Yc, s.Xx100p, s.Xmax540, s.paddingX16]
|
|
10
|
+
page.fixedL = [s.col_cXsY, s.Yc, s.Xx100p, s.Xmax672, s.paddingX16]
|
|
11
|
+
page.fixedXL = [s.col_cXsY, s.Yc, s.Xx100p, s.Xmax960, s.paddingX16]
|
|
12
|
+
page.docs = [s.col_cXsY, s.Yc, s.Xx100p, s.Xmax540, s.paddingX16, s.marginT16]
|
|
13
13
|
page.card = [s.col_cX, s.Yc, s.paddingX16]
|
|
14
14
|
|
|
15
15
|
return page
|
|
@@ -21,7 +21,7 @@ export const getRoot = (s, c, t) => {
|
|
|
21
21
|
root.cards.text.title = [s.textS, s.fs16, s.lh24, s.fw700]
|
|
22
22
|
root.cards.text.description = [s.textS, s.fs16, s.lh20, s.fw400]
|
|
23
23
|
root.cards.text.label = [s.textS, s.fs16, s.lh24, s.fw600]
|
|
24
|
-
root.cards.box = [s.col, s.
|
|
24
|
+
root.cards.box = [s.col, s.X100p]
|
|
25
25
|
root.cards.top = [s.col, s.paddingT16, s.paddingB8]
|
|
26
26
|
root.cards.title = [...root.cards.text.title, c.item_NN_B400]
|
|
27
27
|
root.cards.description = [...root.cards.text.description, c.item_NN_B400]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class Pulse {
|
|
2
|
+
|
|
3
|
+
get = async () => this.formatDate(new Date())
|
|
4
|
+
|
|
5
|
+
formatDate = date => {
|
|
6
|
+
const parts = new Intl.DateTimeFormat('en-GB', {
|
|
7
|
+
weekday: 'short',
|
|
8
|
+
day: '2-digit',
|
|
9
|
+
month: 'short',
|
|
10
|
+
year: 'numeric',
|
|
11
|
+
hour: '2-digit',
|
|
12
|
+
minute: '2-digit',
|
|
13
|
+
hour12: false,
|
|
14
|
+
timeZone: 'UTC'
|
|
15
|
+
}).formatToParts(date)
|
|
16
|
+
|
|
17
|
+
const value = type => parts.find(part => part.type === type)?.value ?? ''
|
|
18
|
+
return `${value('weekday')} ${value('day')} ${value('month')} ${value('year')} ${value('hour')}:${value('minute')}`
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { Pulse }
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { Router } from '#@/index.dep.js'
|
|
2
2
|
import { middleware } from '#@/server/middleware.js'
|
|
3
3
|
import { resolver } from '#@/server/resolvers.js'
|
|
4
|
+
import { Pulse } from '#@/modules/pulse.js'
|
|
4
5
|
import { sync } from '#@/modules/sync.server.js'
|
|
5
6
|
import { clone } from '#@/modules/clone.js'
|
|
6
7
|
|
|
7
8
|
const rs = {}
|
|
8
9
|
|
|
10
|
+
// pulse
|
|
11
|
+
|
|
12
|
+
rs.pulse = {}
|
|
13
|
+
rs.pulse.method = 'get'
|
|
14
|
+
rs.pulse.name = 'pulse'
|
|
15
|
+
rs.pulse.path = '/pulse'
|
|
16
|
+
rs.pulse.resolve = async (...args) => await resolver.resolve(new Pulse().get, rs.pulse.path, ...args)
|
|
17
|
+
rs.pulse.funcs = [ rs.pulse.resolve ]
|
|
18
|
+
|
|
9
19
|
rs.sync_current = {}
|
|
10
20
|
rs.sync_current.method = 'post'
|
|
11
21
|
rs.sync_current.name = 'sync_current'
|
|
@@ -55,4 +65,4 @@ rs.clone.funcs = [rs.clone.auth, rs.clone.resolve]
|
|
|
55
65
|
|
|
56
66
|
const router = Router()
|
|
57
67
|
for ( const r of Object.values(rs)) router[r.method](r.path, ...r.funcs)
|
|
58
|
-
export { router, rs as routes }
|
|
68
|
+
export { router, rs as routes }
|
|
@@ -15,6 +15,12 @@
|
|
|
15
15
|
"input": "media/trademarks",
|
|
16
16
|
"output": "get/media/trademarks"
|
|
17
17
|
},
|
|
18
|
+
"media/graphic": {
|
|
19
|
+
"worker": "media_graphic",
|
|
20
|
+
"input": "media/graphic",
|
|
21
|
+
"output": "get/media/graphic",
|
|
22
|
+
"file": "get/data/root/statics/@graphic.json"
|
|
23
|
+
},
|
|
18
24
|
"media/images": {
|
|
19
25
|
"worker": "media_images",
|
|
20
26
|
"input": "media/images",
|
|
@@ -25,6 +31,11 @@
|
|
|
25
31
|
"input": "media/covers",
|
|
26
32
|
"output": "get/media/covers"
|
|
27
33
|
},
|
|
34
|
+
"docs/pdf": {
|
|
35
|
+
"worker": "docs_pdf",
|
|
36
|
+
"input": "docs/pdf",
|
|
37
|
+
"output": "get/docs/pdf"
|
|
38
|
+
},
|
|
28
39
|
"data/root": {
|
|
29
40
|
"worker": "data_statics",
|
|
30
41
|
"input": "data/root",
|
|
@@ -51,7 +62,8 @@
|
|
|
51
62
|
"isPublisher": true,
|
|
52
63
|
"worker": "tabulars",
|
|
53
64
|
"input":"tabulars_data_content",
|
|
54
|
-
"output": "get/data/content"
|
|
65
|
+
"output": "get/data/content",
|
|
66
|
+
"outputModules": "get/data/modules"
|
|
55
67
|
}
|
|
56
68
|
}
|
|
57
|
-
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const help = `
|
|
2
|
+
@fullstackunicorn/create-root
|
|
3
|
+
|
|
4
|
+
Create a new FullstackUnicorn root scaffold.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
yarn create @fullstackunicorn/root <foldername>
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
yarn create @fullstackunicorn/root example.project.dev
|
|
11
|
+
|
|
12
|
+
What this command does:
|
|
13
|
+
- creates <foldername> in the current working directory
|
|
14
|
+
- copies the bundled root scaffold
|
|
15
|
+
- does not initialize dependencies
|
|
16
|
+
- does not copy client data
|
|
17
|
+
- does not run root upgrade
|
|
18
|
+
|
|
19
|
+
After create-root:
|
|
20
|
+
1. copy the prepared client root.config.app.js into <foldername>/root.config.app.js
|
|
21
|
+
2. copy prepared icons into <foldername>/workspace/volume/media/icons/
|
|
22
|
+
3. copy prepared trademarks into <foldername>/workspace/volume/media/trademarks/
|
|
23
|
+
4. cd <foldername>
|
|
24
|
+
5. yarn initialize:all
|
|
25
|
+
6. yarn root upgrade
|
|
26
|
+
|
|
27
|
+
AI safety rules:
|
|
28
|
+
- always ask for the target parent path before running this command
|
|
29
|
+
- always ask for the client data folder before initialization or root upgrade
|
|
30
|
+
- the client data folder must contain root.config.app.js, icons/, and trademarks/
|
|
31
|
+
- do not run this command with --help, -help, or help as a folder name
|
|
32
|
+
- options beginning with "-" are not valid folder names
|
|
33
|
+
`
|