hfs 0.26.8 → 0.27.2

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 (163) hide show
  1. package/README.md +15 -2
  2. package/admin/assets/index-509bb1d6.js +415 -0
  3. package/admin/assets/index-60a380a7.css +1 -0
  4. package/admin/assets/sha512-738f0943.js +8 -0
  5. package/admin/index.html +3 -1
  6. package/admin/{public/logo.svg → logo.svg} +0 -0
  7. package/frontend/assets/index-6e178dfd.css +1 -0
  8. package/frontend/assets/index-aea7654e.js +85 -0
  9. package/frontend/assets/sha512-bf915587.js +8 -0
  10. package/frontend/{public/fontello.css → fontello.css} +0 -0
  11. package/frontend/{public/fontello.woff2 → fontello.woff2} +0 -0
  12. package/frontend/index.html +4 -2
  13. package/package.json +2 -6
  14. package/plugins/vhosting/plugin.js +23 -20
  15. package/src/QuickZipStream.js +285 -0
  16. package/src/ThrottledStream.js +93 -0
  17. package/src/adminApis.js +169 -0
  18. package/src/api.accounts.js +59 -0
  19. package/src/api.auth.js +128 -0
  20. package/src/api.file_list.js +110 -0
  21. package/src/api.helpers.js +32 -0
  22. package/src/api.monitor.js +104 -0
  23. package/src/api.plugins.js +128 -0
  24. package/src/api.vfs.js +167 -0
  25. package/src/apiMiddleware.js +123 -0
  26. package/src/block.js +34 -0
  27. package/src/commands.js +125 -0
  28. package/src/config.js +168 -0
  29. package/src/connections.js +57 -0
  30. package/src/const.js +94 -0
  31. package/src/crypt.js +21 -0
  32. package/src/debounceAsync.js +49 -0
  33. package/src/events.js +9 -0
  34. package/src/frontEndApis.js +38 -0
  35. package/src/github.js +104 -0
  36. package/src/index.js +57 -0
  37. package/src/listen.js +235 -0
  38. package/src/log.js +137 -0
  39. package/src/middlewares.js +195 -0
  40. package/src/misc.js +160 -0
  41. package/src/pbkdf2.js +74 -0
  42. package/src/perm.js +183 -0
  43. package/src/plugins.js +343 -0
  44. package/src/serveFile.js +105 -0
  45. package/src/serveGuiFiles.js +113 -0
  46. package/src/sse.js +30 -0
  47. package/src/throttler.js +91 -0
  48. package/src/update.js +70 -0
  49. package/src/util-files.js +163 -0
  50. package/src/util-generators.js +31 -0
  51. package/src/util-http.js +32 -0
  52. package/src/vfs.js +232 -0
  53. package/src/watchLoad.js +73 -0
  54. package/src/zip.js +73 -0
  55. package/admin/.DS_Store +0 -0
  56. package/admin/.eslintrc +0 -8
  57. package/admin/.gitignore +0 -23
  58. package/admin/package.json +0 -67
  59. package/admin/src/AccountForm.ts +0 -92
  60. package/admin/src/AccountsPage.ts +0 -143
  61. package/admin/src/App.ts +0 -83
  62. package/admin/src/ArrayField.ts +0 -84
  63. package/admin/src/ConfigPage.ts +0 -279
  64. package/admin/src/FileField.ts +0 -52
  65. package/admin/src/FileForm.ts +0 -148
  66. package/admin/src/FilePicker.ts +0 -166
  67. package/admin/src/HomePage.ts +0 -96
  68. package/admin/src/InstalledPlugins.ts +0 -158
  69. package/admin/src/LoginRequired.ts +0 -75
  70. package/admin/src/LogoutPage.ts +0 -27
  71. package/admin/src/LogsPage.ts +0 -75
  72. package/admin/src/MainMenu.ts +0 -74
  73. package/admin/src/MenuButton.ts +0 -38
  74. package/admin/src/MonitorPage.ts +0 -200
  75. package/admin/src/OnlinePlugins.ts +0 -101
  76. package/admin/src/PermField.ts +0 -80
  77. package/admin/src/PluginsPage.ts +0 -27
  78. package/admin/src/VfsMenuBar.ts +0 -58
  79. package/admin/src/VfsPage.ts +0 -124
  80. package/admin/src/VfsTree.ts +0 -95
  81. package/admin/src/addFiles.ts +0 -59
  82. package/admin/src/api.ts +0 -246
  83. package/admin/src/dialog.ts +0 -203
  84. package/admin/src/index.css +0 -21
  85. package/admin/src/index.ts +0 -10
  86. package/admin/src/md.ts +0 -31
  87. package/admin/src/misc.ts +0 -141
  88. package/admin/src/react-app-env.d.ts +0 -1
  89. package/admin/src/reportWebVitals.ts +0 -15
  90. package/admin/src/setupTests.ts +0 -5
  91. package/admin/src/state.ts +0 -40
  92. package/admin/src/theme.ts +0 -37
  93. package/admin/tsconfig.json +0 -26
  94. package/admin/vite.config.ts +0 -32
  95. package/frontend/.DS_Store +0 -0
  96. package/frontend/.eslintrc +0 -8
  97. package/frontend/.gitignore +0 -23
  98. package/frontend/package.json +0 -51
  99. package/frontend/src/App.ts +0 -25
  100. package/frontend/src/Breadcrumbs.ts +0 -43
  101. package/frontend/src/BrowseFiles.ts +0 -141
  102. package/frontend/src/Head.ts +0 -45
  103. package/frontend/src/UserPanel.ts +0 -52
  104. package/frontend/src/api.ts +0 -78
  105. package/frontend/src/components.ts +0 -54
  106. package/frontend/src/dialog.css +0 -76
  107. package/frontend/src/dialog.ts +0 -105
  108. package/frontend/src/icons.ts +0 -46
  109. package/frontend/src/index.scss +0 -307
  110. package/frontend/src/index.ts +0 -10
  111. package/frontend/src/login.ts +0 -50
  112. package/frontend/src/menu.ts +0 -188
  113. package/frontend/src/misc.ts +0 -54
  114. package/frontend/src/options.ts +0 -52
  115. package/frontend/src/react-app-env.d.ts +0 -1
  116. package/frontend/src/reportWebVitals.ts +0 -15
  117. package/frontend/src/setupTests.ts +0 -5
  118. package/frontend/src/state.ts +0 -82
  119. package/frontend/src/useAuthorized.ts +0 -17
  120. package/frontend/src/useFetchList.ts +0 -144
  121. package/frontend/src/useTheme.ts +0 -23
  122. package/frontend/tsconfig.json +0 -26
  123. package/frontend/vite.config.ts +0 -21
  124. package/src/QuickZipStream.ts +0 -279
  125. package/src/ThrottledStream.ts +0 -98
  126. package/src/adminApis.ts +0 -161
  127. package/src/api.accounts.ts +0 -78
  128. package/src/api.auth.ts +0 -131
  129. package/src/api.file_list.ts +0 -102
  130. package/src/api.helpers.ts +0 -30
  131. package/src/api.monitor.ts +0 -106
  132. package/src/api.plugins.ts +0 -139
  133. package/src/api.vfs.ts +0 -182
  134. package/src/apiMiddleware.ts +0 -124
  135. package/src/block.ts +0 -35
  136. package/src/commands.ts +0 -122
  137. package/src/config.ts +0 -166
  138. package/src/connections.ts +0 -60
  139. package/src/const.ts +0 -57
  140. package/src/crypt.ts +0 -16
  141. package/src/debounceAsync.ts +0 -51
  142. package/src/events.ts +0 -6
  143. package/src/frontEndApis.ts +0 -17
  144. package/src/github.ts +0 -102
  145. package/src/index.ts +0 -53
  146. package/src/listen.ts +0 -220
  147. package/src/log.ts +0 -128
  148. package/src/middlewares.ts +0 -176
  149. package/src/misc.ts +0 -149
  150. package/src/pbkdf2.ts +0 -83
  151. package/src/perm.ts +0 -194
  152. package/src/plugins.ts +0 -342
  153. package/src/serveFile.ts +0 -104
  154. package/src/serveGuiFiles.ts +0 -95
  155. package/src/sse.ts +0 -29
  156. package/src/throttler.ts +0 -106
  157. package/src/update.ts +0 -67
  158. package/src/util-files.ts +0 -137
  159. package/src/util-generators.ts +0 -29
  160. package/src/util-http.ts +0 -29
  161. package/src/vfs.ts +0 -258
  162. package/src/watchLoad.ts +0 -75
  163. package/src/zip.ts +0 -69
@@ -1,59 +0,0 @@
1
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
-
3
- import { alertDialog, newDialog, promptDialog } from './dialog'
4
- import { createElement as h, Fragment } from 'react'
5
- import { Box } from '@mui/material'
6
- import { VfsNode, reloadVfs } from './VfsPage'
7
- import { state } from './state'
8
- import { apiCall } from './api'
9
- import FilePicker from './FilePicker'
10
- import { onlyTruthy } from './misc'
11
-
12
- export default function addFiles() {
13
- const close = newDialog({
14
- title: "Add files or folders",
15
- dialogProps: { sx:{ minWidth: 'min(90vw, 40em)', minHeight: 'calc(100vh - 9em)' } },
16
- Content() {
17
- const under = getUnder()
18
- return h(Fragment, {},
19
- h(Box, { sx:{ typography: 'body1', px: 1, py: 2 } },
20
- "Selected elements will be added to virtual path " + (under || '(home)')),
21
- h(FilePicker, {
22
- async onSelect(sel) {
23
- let failed = await Promise.all(sel.map(source =>
24
- apiCall('add_vfs', { under, source }).then(() => '', () => source) ))
25
- failed = onlyTruthy(failed)
26
- if (failed.length)
27
- await alertDialog("Some elements have been rejected: "+failed.join(', '), 'error')
28
- reloadVfs()
29
- close()
30
- }
31
- })
32
- )
33
- }
34
- })
35
- }
36
-
37
- export async function addVirtual() {
38
- try {
39
- const name = await promptDialog("Enter folder name")
40
- if (!name) return
41
- const under = getUnder()
42
- await apiCall('add_vfs', { under, name })
43
- reloadVfs([ (under||'') + '/' + name ])
44
- await alertDialog(`Folder "${name}" created`, 'success')
45
- }
46
- catch(e) {
47
- await alertDialog(e as Error)
48
- }
49
- }
50
-
51
- function getUnder() {
52
- let f: VfsNode | undefined = state.selectedFiles[0]
53
- if (!f)
54
- return ''
55
- if (f.type !== 'folder')
56
- f = f.parent
57
- const { id } = f!
58
- return id === '/' ? '' : id
59
- }
package/admin/src/api.ts DELETED
@@ -1,246 +0,0 @@
1
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
-
3
- import { createElement as h, useCallback, useEffect, useMemo, useRef } from 'react'
4
- import { Dict, err2msg, Falsy, getCookie, IconBtn, spinner, useStateMounted, wantArray } from './misc'
5
- import { Alert } from '@mui/material'
6
- import _ from 'lodash'
7
- import { state } from './state'
8
- import { Refresh } from '@mui/icons-material'
9
- import produce, { Draft } from 'immer'
10
-
11
- export function useApiEx<T=any>(...args: Parameters<typeof useApi>) {
12
- const [data, error, reload] = useApi<T>(...args)
13
- const cmd = args[0]
14
- const loading = data === undefined
15
- const element = useMemo(() =>
16
- !cmd ? null
17
- : error ? h(Alert, { severity: 'error' }, String(error), h(IconBtn, { icon: Refresh, onClick: reload, sx: { m:'-8px 0 -8px 16px' } }))
18
- : loading ? spinner()
19
- : null,
20
- [error, cmd, loading, reload])
21
- return { data, error, reload, loading, element }
22
- }
23
-
24
- const PREFIX = '/~/api/'
25
-
26
- const timeoutByApi: Dict = {
27
- get_status: 20 // can be lengthy on slow machines because of the find-process-on-busy-port feature
28
- }
29
- export function apiCall(cmd: string, params?: Dict, { timeout=undefined }={}) : Promise<any> {
30
- const csrf = getCsrf()
31
- if (csrf)
32
- params = { csrf, ...params }
33
-
34
- const controller = new AbortController()
35
- if (timeout !== false)
36
- setTimeout(() => controller.abort('timeout'), 1000*(timeoutByApi[cmd] ?? timeout ?? 10))
37
- return fetch(PREFIX+cmd, {
38
- method: 'POST',
39
- headers: { 'content-type': 'application/json' },
40
- signal: controller.signal,
41
- body: params && JSON.stringify(params),
42
- }).then(async res => {
43
- if (res.ok)
44
- return res.json().then(json => {
45
- console.debug('API', cmd, params, '>>', json)
46
- return json
47
- })
48
- const msg = await res.text() || 'Failed API ' + cmd
49
- console.warn(msg + (params ? ' ' + JSON.stringify(params) : ''))
50
- if (res.status === 401)
51
- state.loginRequired = true
52
- throw new ApiError(res.status, msg)
53
- }, err => {
54
- if (err?.message?.includes('fetch'))
55
- throw Error("Network error")
56
- throw err
57
- })
58
- }
59
-
60
- export class ApiError extends Error {
61
- constructor(readonly code:number, message: string) {
62
- super(message);
63
- }
64
- }
65
-
66
- export function useApi<T=any>(cmd: string | Falsy, params?: object) : [T | undefined, undefined | Error, ()=>void] {
67
- const [ret, setRet] = useStateMounted<T | undefined>(undefined)
68
- const [err, setErr] = useStateMounted<Error | undefined>(undefined)
69
- const [forcer, setForcer] = useStateMounted(0)
70
- const loadingRef = useRef(false)
71
- useEffect(()=>{
72
- setRet(undefined)
73
- setErr(undefined)
74
- if (!cmd) return
75
- loadingRef.current = true
76
- apiCall(cmd, params)
77
- .then(setRet, setErr)
78
- .finally(()=> loadingRef.current = false)
79
- }, [cmd, JSON.stringify(params), forcer]) //eslint-disable-line -- json-ize to detect deep changes
80
- const reload = useCallback(()=> loadingRef.current || setForcer(v => v+1), [setForcer])
81
- return [ret, err, reload]
82
- }
83
-
84
- type EventHandler = (type:string, data?:any) => void
85
-
86
- export function apiEvents(cmd: string, params: Dict, cb:EventHandler) {
87
- console.debug('API EVENTS', cmd, params)
88
- const csrf = getCsrf()
89
- const processed: Record<string,string> = { csrf: csrf && JSON.stringify(csrf) }
90
- for (const k in params) {
91
- const v = params[k]
92
- if (v === undefined) continue
93
- processed[k] = JSON.stringify(v)
94
- }
95
- const source = new EventSource(PREFIX + cmd + '?' + new URLSearchParams(processed))
96
- source.onopen = () => cb('connected')
97
- source.onerror = err => cb('error', err)
98
- source.onmessage = ({ data }) => {
99
- if (!data) {
100
- cb('closed')
101
- return source.close()
102
- }
103
- try { data = JSON.parse(data) }
104
- catch {
105
- return cb('string', data)
106
- }
107
- console.debug('SSE msg', data)
108
- cb('msg', data)
109
- }
110
- return source
111
- }
112
-
113
- function getCsrf() {
114
- return getCookie('csrf')
115
- }
116
-
117
- export function useApiEvents(cmd: string, params: Dict={}) {
118
- const [data, setData] = useStateMounted<any>(undefined)
119
- const [error, setError] = useStateMounted<any>(undefined)
120
- const [loading, setLoading] = useStateMounted(false)
121
- useEffect(() => {
122
- const src = apiEvents(cmd, params, (type, data) => {
123
- switch (type) {
124
- case 'error':
125
- setError("Connection error")
126
- return stop()
127
- case 'closed':
128
- return stop()
129
- case 'msg':
130
- if (src?.readyState === src?.CLOSED)
131
- return stop()
132
- return setData(data)
133
- }
134
- })
135
- return () => {
136
- src.close()
137
- stop()
138
- }
139
-
140
- function stop() {
141
- setLoading(false)
142
- }
143
- }, [cmd, JSON.stringify(params)]) //eslint-disable-line
144
- return { data, loading, error }
145
- }
146
-
147
- export function useApiList<T=any>(cmd:string|Falsy, params: Dict={}, { addId=false, map=((x:any)=>x) }={}) {
148
- const [list, setList] = useStateMounted<T[]>([])
149
- const [error, setError] = useStateMounted<any>(undefined)
150
- const [connecting, setConnecting] = useStateMounted(true)
151
- const [loading, setLoading] = useStateMounted(false)
152
- const [initializing, setInitializing] = useStateMounted(true)
153
- const idRef = useRef(0)
154
- useEffect(() => {
155
- if (!cmd) return
156
- const buffer: T[] = []
157
- const apply = _.debounce(() => {
158
- const chunk = buffer.splice(0, Infinity)
159
- if (chunk.length)
160
- setList(list => [ ...list, ...chunk ])
161
- }, 1000, { maxWait: 1000 })
162
- setError(undefined)
163
- setLoading(true)
164
- setConnecting(true)
165
- setInitializing(true)
166
- setList([])
167
- const src = apiEvents(cmd, params, (type, data) => {
168
- switch (type) {
169
- case 'connected':
170
- setConnecting(false)
171
- return setTimeout(() => apply.flush()) // this trick we'll cause first entries to be rendered almost immediately, while the rest will be subject to normal debouncing
172
- case 'error':
173
- setError("Connection error")
174
- return stop()
175
- case 'closed':
176
- return stop()
177
- case 'msg':
178
- wantArray(data).forEach(data => {
179
- if (data === 'ready') {
180
- apply.flush()
181
- setInitializing(false)
182
- return
183
- }
184
- if (data.error)
185
- return setError(err2msg(data.error))
186
- if (data.add) {
187
- const rec = map(data.add)
188
- if (addId)
189
- rec.id = ++idRef.current
190
- buffer.push(rec)
191
- apply()
192
- return
193
- }
194
- if (data.remove) {
195
- const matchOnList: ReturnType<typeof _.matches>[] = []
196
- // first remove from the buffer
197
- for (const key of data.remove) {
198
- const match1 = _.matches(key)
199
- if (_.isEmpty(_.remove(buffer, match1)))
200
- matchOnList.push(match1)
201
- }
202
- // then work the hooked state
203
- if (_.isEmpty(matchOnList))
204
- return
205
- setList(list => {
206
- const filtered = list.filter(rec => !matchOnList.some(match1 => match1(rec)))
207
- return filtered.length < list.length ? filtered : list // avoid unnecessary changes
208
- })
209
- return
210
- }
211
- if (data.update) {
212
- apply.flush() // avoid treating buffer
213
- setList(list => {
214
- const modified = [...list]
215
- for (const { search, change } of data.update) {
216
- const idx = modified.findIndex(_.matches(search))
217
- if (idx >= 0)
218
- modified[idx] = { ...modified[idx], ...change }
219
- }
220
- return modified
221
- })
222
- return
223
- }
224
- console.debug('unknown api event', type, data)
225
- })
226
- if (src?.readyState === src?.CLOSED)
227
- stop()
228
- }
229
- })
230
-
231
- return () => src.close()
232
-
233
- function stop() {
234
- setInitializing(false)
235
- setLoading(false)
236
- apply.flush()
237
- }
238
- }, [cmd, JSON.stringify(params)]) //eslint-disable-line
239
- return { list, loading, error, initializing, connecting, setList, updateList }
240
-
241
- function updateList(cb: (toModify: Draft<typeof list>) => void) {
242
- setList(produce(list, x => {
243
- cb(x)
244
- }))
245
- }
246
- }
@@ -1,203 +0,0 @@
1
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
-
3
- import {
4
- Box,
5
- Button,
6
- CircularProgress,
7
- Dialog as MuiDialog,
8
- DialogContent,
9
- DialogProps,
10
- DialogTitle,
11
- IconButton
12
- } from '@mui/material'
13
- import {
14
- createElement as h, Fragment,
15
- isValidElement,
16
- ReactElement,
17
- useEffect,
18
- useRef,
19
- useState
20
- } from 'react'
21
- import { Check, Close, Error as ErrorIcon, Forward, Info, Warning } from '@mui/icons-material'
22
- import { newDialog, closeDialog, dialogsDefaults, DialogOptions } from '@hfs/shared'
23
- import { Form, FormProps } from '@hfs/mui-grid-form'
24
- import { useBreakpoint } from './misc'
25
- import { Flex } from '@hfs/frontend/src/components'
26
- export * from '@hfs/shared/lib/dialogs'
27
-
28
- dialogsDefaults.Container = function Container(d:DialogOptions) {
29
- useEffect(()=>{
30
- ref.current?.focus()
31
- }, [])
32
- const ref = useRef<HTMLElement>()
33
- d = { ...dialogsDefaults, ...d }
34
- const { sx, root, ...rest } = d.dialogProps||{}
35
- const p = d.padding ? 2 : 0
36
- return h(MuiDialog, {
37
- open: true,
38
- maxWidth: 'lg',
39
- fullScreen: !useBreakpoint('sm'),
40
- ...rest,
41
- ...root,
42
- onClose: ()=> closeDialog(),
43
- },
44
- d.title && h(DialogTitle, {}, d.title),
45
- h(DialogContent, {
46
- ref,
47
- sx: { ...sx, px: p, pb: p, display: 'flex', flexDirection: 'column', }
48
- }, h(d.Content) )
49
- )
50
- }
51
-
52
- type AlertType = 'error' | 'warning' | 'info' | 'success'
53
-
54
- const type2ico = {
55
- error: ErrorIcon,
56
- warning: Warning,
57
- info: Info,
58
- success: Check,
59
- }
60
- export async function alertDialog(msg: ReactElement | string | Error, options?: AlertType | ({ type?:AlertType, icon?: ReactElement } & Partial<DialogOptions>)) {
61
- return new Promise(resolve => {
62
- const opt = typeof options === 'string' ? { type: options } : (options ?? {})
63
- let { type='info', ...rest } = opt
64
- if (msg instanceof Error) {
65
- msg = msg.message || String(msg)
66
- type = 'error'
67
- }
68
- const close = newDialog({
69
- className: 'dialog-alert-' + type,
70
- icon: '!',
71
- onClose: resolve,
72
- ...rest,
73
- Content() {
74
- return h(Box, { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 1 },
75
- h(IconButton, {
76
- onClick() {
77
- close()
78
- },
79
- size: 'small',
80
- sx: { position: 'absolute', right: 0, top: 0, opacity: .5 }
81
- }, h(Close)),
82
- opt.icon ?? h(type2ico[type], { color: type, fontSize: 'large' }),
83
- isValidElement(msg) ? msg
84
- : h(Box, { fontSize: 'large', mb: 1, lineHeight: '1.8em' }, String(msg)),
85
- )
86
- }
87
- })
88
- })
89
- }
90
-
91
- interface ConfirmOptions { href?: string }
92
- export async function confirmDialog(msg: string | ReactElement, { href }: ConfirmOptions={}) : Promise<boolean> {
93
- return new Promise(resolve => newDialog({
94
- className: 'dialog-confirm',
95
- icon: '?',
96
- onClose: resolve,
97
- Content
98
- }) )
99
-
100
- function Content() {
101
- return h(Fragment, {},
102
- h(Box, { mb: 2 }, msg),
103
- h(Flex, {},
104
- h('a', {
105
- href,
106
- onClick: () => closeDialog(true),
107
- }, h(Button, { variant: 'contained' }, "Confirm")),
108
- h(Button, { onClick: () => closeDialog(false) }, "Don't"),
109
- ),
110
- )
111
- }
112
- }
113
-
114
- type FormDialog<T> = Pick<DialogProps, 'fullScreen' | 'title'>
115
- & Pick<DialogOptions, 'dialogProps'>
116
- & Omit<FormProps<T>, 'values' | 'save' | 'set'>
117
- & Partial<Pick<FormProps<T>, 'values' | 'save'>>
118
- & {
119
- onChange?: (values:Partial<T>, extra: { setValues: React.Dispatch<React.SetStateAction<Partial<T>>> }) => void,
120
- before?: any
121
- }
122
- export async function formDialog<T>({ fullScreen, title, onChange, before, ...props }: FormDialog<T>) : Promise<T> {
123
- return new Promise(resolve => newDialog({
124
- className: 'dialog-confirm',
125
- icon: '?',
126
- onClose: resolve,
127
- title,
128
- Content
129
- }) )
130
-
131
- function Content() {
132
- const [values, setValues] = useState<Partial<T>>(props.values||{})
133
- return h(Fragment, {},
134
- before,
135
- h(Form, {
136
- ...props,
137
- values,
138
- set(v, k) {
139
- const newV = { ...values, [k]: v }
140
- setValues(newV)
141
- onChange?.(newV, { setValues })
142
- },
143
- save: {
144
- ...props.save,
145
- onClick() {
146
- closeDialog(values)
147
- }
148
- }
149
- })
150
- )
151
- }
152
- }
153
-
154
- export async function promptDialog(msg: string, props:any={}) : Promise<string | undefined> {
155
- return formDialog<{ text: string }>({
156
- ...props,
157
- fields: [
158
- { k: 'text', label: null, autoFocus: true,
159
- before: h(Box, { mb: 2 }, msg),
160
- ...props.field
161
- },
162
- ],
163
- save: {
164
- children: "Continue",
165
- startIcon: h(Forward),
166
- ...props.save,
167
- },
168
- saveOnEnter: true,
169
- barSx: { gap: 2 },
170
- addToBar: [
171
- h(Button, { onClick: closeDialog }, "Cancel"),
172
- ...props.addToBar||[],
173
- ]
174
- }).then(values => values?.text)
175
- }
176
-
177
- export function waitDialog() {
178
- return newDialog({ Content: CircularProgress, closable: false })
179
- }
180
-
181
- export function toast(msg: string | ReactElement, type: AlertType | ReactElement='info') {
182
- const ms = 3000
183
- const close = newDialog({
184
- Content,
185
- dialogProps: {
186
- PaperProps: {
187
- sx: { transition: `opacity ${ms}ms ease-in` },
188
- ref(x: HTMLElement) { // we need to set opacity later, to trigger transition
189
- if (x)
190
- x.style.opacity = '0'
191
- }
192
- }
193
- }
194
- })
195
- setTimeout(close, ms)
196
-
197
- function Content(){
198
- return h(Box, { display:'flex', flexDirection: 'column', alignItems: 'center', gap: 1 },
199
- isValidElement(type) ? type : h(type2ico[type], { color:type }),
200
- isValidElement(msg) ? msg : h('div', {}, String(msg))
201
- )
202
- }
203
- }
@@ -1,21 +0,0 @@
1
- body {
2
- margin: 0;
3
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5
- sans-serif;
6
- -webkit-font-smoothing: antialiased;
7
- -moz-osx-font-smoothing: grayscale;
8
- height: 100vh;
9
- }
10
- #root { min-height: 100%; display:flex; }
11
-
12
- code {
13
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
14
- monospace;
15
- }
16
-
17
- .MuiTreeItem-content {
18
- box-sizing: border-box; /* avoid unwanted scrolling caused by its width:100% + padding */
19
- }
20
-
21
- ol, ul { margin-top: .2em }
@@ -1,10 +0,0 @@
1
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
-
3
- import { createElement as h, StrictMode } from 'react'
4
- import { createRoot } from 'react-dom/client'
5
- import './index.css'
6
- import '@hfs/shared/src/min-crypto-polyfill'
7
- import App from './App'
8
-
9
- createRoot(document.getElementById('root')!)
10
- .render( h(StrictMode, {}, h(App)) )
package/admin/src/md.ts DELETED
@@ -1,31 +0,0 @@
1
- // This file is part of HFS - Copyright 2021-2022, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
-
3
- import { createElement as h, Fragment } from 'react'
4
-
5
- // markdown inspired syntax to transform text into react elements: * for bold, / for italic, _ for underline, ` for code
6
- export default function md(text: string | TemplateStringsArray) {
7
- if (typeof text !== 'string')
8
- text = text[0]
9
- const re = /([`*/_])(.+)\1|(\n)/g
10
- const res = []
11
- let last = 0
12
- let match
13
- while (match = re.exec(text)) { //eslint-disable-line no-cond-assign
14
- res.push( text.slice(last, match.index) )
15
- if (match[3])
16
- res.push(h('br'))
17
- else {
18
- const tag = ({
19
- '`': 'code',
20
- '*': 'b',
21
- '/': 'i',
22
- '_': 'u',
23
- })[ match[1] ]
24
- if (!tag)
25
- throw Error("should never happen")
26
- res.push( h(tag,{}, match[2]) )
27
- }
28
- last = match.index + match[0].length
29
- }
30
- return h(Fragment, {}, ...res, text.slice(last, Infinity))
31
- }