plugins: exports.preview
Massimo Melina committed
May 16, 2024 at 13:45 UTC
222b0727a87cddef4dccbc527d55c20982a9fe2e
3 files changed
+29
-5
admin/src/OnlinePlugins.ts
+25
-4
@@ -3,8 +3,8 @@
3
import { apiCall, useApiList } from './api'
4
import { Fragment, createElement as h, useState } from 'react'
5
import { DataTable } from './DataTable'
6
-import { HTTP_FAILED_DEPENDENCY, xlate } from './misc'
7
-import { Download, Search } from '@mui/icons-material'
6
+import { HTTP_FAILED_DEPENDENCY, newDialog, wantArray, xlate } from './misc'
7
+import { ArrowBack, ArrowForward, Download, RemoveRedEye, Search } from '@mui/icons-material'
8
import { StringField } from '@hfs/mui-grid-form'
9
import { useDebounce } from 'usehooks-ts'
10
import { descriptionField, renderName, startPlugin, themeField } from './InstalledPlugins'
@@ -12,7 +12,7 @@ import { state, useSnapState } from './state'
12
import { alertDialog, confirmDialog, toast } from './dialog'
13
import _ from 'lodash'
14
import { PLUGIN_ERRORS } from './PluginsPage'
15
-import { IconBtn } from './mui'
15
+import { Flex, IconBtn } from './mui'
16
17
export default function OnlinePlugins() {
18
const [search, setSearch] = useState('')
@@ -88,7 +88,16 @@ export default function OnlinePlugins() {
88
return alertDialog(msg, 'error')
89
})
90
}
91
- })
91
+ }),
92
+ h(IconBtn, {
93
+ icon: RemoveRedEye,
94
+ disabled: !row.preview,
95
+ onClick: () => newDialog({
96
+ title: id,
97
+ dialogProps: { sx: { minHeight: '50vh', minWidth: '50vw' } }, // the image will use available space, so we must reserve it (while mobile is going full-screen)
98
+ Content: () => h(ShowImages, { imgs: wantArray(row.preview) })
99
+ })
100
+ }),
101
]
102
})
103
)
@@ -115,3 +124,15 @@ export default function OnlinePlugins() {
124
}
125
}
126
127
+function ShowImages({ imgs }: { imgs: string[] }) {
128
+ const [cur, setCur] = useState(0)
129
+ return h(Flex, { vert: true, flex: 1 },
130
+ h(Flex, { vert: true, center: true, height: 0, flex: 'auto' },
131
+ h('img', { src: imgs[cur], style: { margin: 'auto', /*center*/ maxWidth: '100%', maxHeight: '100%' /*limit*/ } }),
132
+ ),
133
+ imgs.length > 1 && h(Flex, { center: true },
134
+ h(IconBtn, { icon: ArrowBack, disabled: !cur, onClick: () => setCur(cur - 1) }),
135
+ h(IconBtn, { icon: ArrowForward, disabled: cur >= imgs.length - 1, onClick: () => setCur(cur + 1) }),
136
+ ),
137
+ )
138
+}
\ No newline at end of file
dev-plugins.md
+2
-1
@@ -12,7 +12,7 @@ Normally you'll have a plug-in that's a theme, and another that's a firewall,
12
but nothing is preventing a single plug-in from doing both tasks.
13
14
## Exported object
15
-`plugin.js` is a javascript module, and its main way to communicate with HFS is by exporting things.
15
+`plugin.js` is a javascript module (executed by Node.js), and its main way to communicate with HFS is by exporting things.
16
For example, it can define its description like this
17
```js
18
exports.description = "I'm a nice plugin"
@@ -49,6 +49,7 @@ All the following properties are optional unless otherwise specified.
49
- `isTheme: boolean | "light" | "dark"` set true if this is a theme that's not supposed to work together with other themes.
50
Running a theme will cause other themes to be stopped. Missing this, HFS will check if the name of the plugin ends with `-theme`.
51
Special values "light" and "dark" to declare the theme is (for example) dark and forces HFS to use dark-theme as a base.
52
+- `preview: string | string[]` one or more images you want to show before your plugin is downloaded.
53
- `depend: { repo: string, version: number }[]` declare what other plugins this depends on. (JSON syntax)
54
- `repo: string | object` pointer to a GitHub repo where this plugin is hosted. (JSON syntax)
55
- the string form is for GitHub repos. Example: "rejetto/file-icons"
src/plugins.ts
+2
@@ -262,6 +262,7 @@ export interface CommonPluginInterface {
262
repo?: Repo
263
depend?: Depend
264
isTheme?: boolean | 'light' | 'dark'
265
+ preview?: string | string[]
266
}
267
export interface AvailablePlugin extends CommonPluginInterface {
268
branch?: string
@@ -486,6 +487,7 @@ export function parsePluginSource(id: string, source: string) {
487
pl.version = Number(/exports.version *= *(\d*\.?\d+)/.exec(source)?.[1]) ?? undefined
488
pl.apiRequired = tryJson(/exports.apiRequired *= *([ \d.,[\]]+)/.exec(source)?.[1]) ?? undefined
489
pl.isTheme = tryJson(/exports.isTheme *= *(true|false|"light"|"dark")/.exec(source)?.[1]) ?? (id.endsWith('-theme') || undefined)
490
+ pl.preview = tryJson(/exports.preview *= *(.+)/.exec(source)?.[1]) ?? undefined
491
pl.depend = tryJson(/exports.depend *= *(\[.*\])/m.exec(source)?.[1])?.filter((x: any) =>
492
typeof x.repo === 'string' && x.version === undefined || typeof x.version === 'number'
493
|| console.warn("plugin dependency discarded", x) )