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) )