fluxplugin 1.0.3 → 1.0.4
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/README.md +15 -12
- package/dist/browser/chunks/chunk-AV4KI7LI.js +1792 -0
- package/dist/browser/chunks/{chunk-TL63576R.js → chunk-OAYWYU5O.js} +48 -11
- package/dist/browser/index.js +3 -1
- package/dist/browser/manager-ui.js +386 -185
- package/dist/browser/react.js +46 -2
- package/dist/cli.js +3622 -257
- package/dist/node/chunks/{chunk-EVX2O65A.js → chunk-SDCGUBEW.js} +49 -11
- package/dist/node/chunks/{chunk-C7VWZZOX.js → chunk-ZLA5XUXN.js} +175 -51
- package/dist/node/cli-api.js +742 -76
- package/dist/node/index.js +3 -1
- package/dist/node/main.js +8 -2
- package/dist/node/preload.js +1 -1
- package/dist/types/cli/build.d.ts +5 -0
- package/dist/types/cli/cli.d.ts +2 -0
- package/dist/types/cli/dist.d.ts +18 -0
- package/dist/types/cli/host-templates.d.ts +2 -0
- package/dist/types/cli/store.d.ts +13 -0
- package/dist/types/cli/templates.d.ts +2 -0
- package/dist/types/core/manifest.d.ts +15 -1
- package/dist/types/core/protocol.d.ts +18 -5
- package/dist/types/core/registry.d.ts +11 -0
- package/dist/types/core/routes.d.ts +3 -1
- package/dist/types/main/adapters.d.ts +3 -0
- package/dist/types/main/assets.d.ts +8 -0
- package/dist/types/main/github-registry.d.ts +2 -1
- package/dist/types/main/index.d.ts +1 -0
- package/dist/types/main/registry.d.ts +55 -18
- package/dist/types/main/stores.d.ts +7 -1
- package/dist/types/manager-ui/index.d.ts +35 -3
- package/dist/types/react/base-css.d.ts +6 -2
- package/dist/types/react/http-bridge.d.ts +5 -0
- package/dist/types/react/i18n.d.ts +33 -0
- package/dist/types/react/index.d.ts +5 -0
- package/dist/types/react/markdown.d.ts +2 -1
- package/dist/types/react/media.d.ts +33 -0
- package/dist/types/react/settings.d.ts +6 -0
- package/dist/types/react/sidebar.d.ts +42 -0
- package/dist/types/react/theme.d.ts +42 -4
- package/dist/types/react/ui.d.ts +42 -0
- package/docs/CUSTOMIZING.md +56 -0
- package/docs/STORE.md +19 -0
- package/docs/screenshots/plugin-page.png +0 -0
- package/docs/screenshots/settings-appearance.png +0 -0
- package/docs/screenshots/site-guide-dark.png +0 -0
- package/docs/screenshots/site-home.png +0 -0
- package/docs/screenshots/site-mobile.png +0 -0
- package/docs/screenshots/site-search.png +0 -0
- package/package.json +1 -1
- package/dist/browser/chunks/chunk-VXQJIDXW.js +0 -1232
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React, { type ReactNode } from "react";
|
|
2
|
+
import type { MenuLocation } from "../core/index.js";
|
|
3
|
+
interface SidebarCtx {
|
|
4
|
+
collapsed: boolean;
|
|
5
|
+
setCollapsed(v: boolean): void;
|
|
6
|
+
toggle(): void;
|
|
7
|
+
width: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Owns the collapsed/expanded state (persisted) and publishes the width as the CSS variable `--flux-sidebar-w`,
|
|
11
|
+
* which the shell's grid uses: `grid-cols-[var(--flux-sidebar-w,232px)_minmax(0,1fr)]`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function SidebarProvider({ children, storageKey, expandedWidth, collapsedWidth }: {
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
storageKey?: string;
|
|
16
|
+
expandedWidth?: number;
|
|
17
|
+
collapsedWidth?: number;
|
|
18
|
+
}): React.ReactElement;
|
|
19
|
+
export declare const useSidebar: () => SidebarCtx;
|
|
20
|
+
export interface SidebarEntry {
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
path?: string;
|
|
24
|
+
command?: string;
|
|
25
|
+
/** Built-in icon name, an `{ d }` SVG path, or any React node. */
|
|
26
|
+
icon?: unknown;
|
|
27
|
+
badge?: string | number;
|
|
28
|
+
children?: SidebarEntry[];
|
|
29
|
+
}
|
|
30
|
+
export declare function renderIcon(icon: unknown, size?: number): ReactNode;
|
|
31
|
+
/**
|
|
32
|
+
* Sidebar navigation: host entries (`top` / `bottom`) around the entries plugins add to the `sidebar` menu location.
|
|
33
|
+
* Supports icons, sub-menus (nested `children` or `parent` ids), collapsing to an icon rail (sub-menus become hover flyouts),
|
|
34
|
+
* and active highlighting. Labels of host entries can be translated through their `id` (`menu.<id>`).
|
|
35
|
+
*/
|
|
36
|
+
export declare function SidebarNav({ top, bottom, location, footer }: {
|
|
37
|
+
top?: SidebarEntry[];
|
|
38
|
+
bottom?: SidebarEntry[];
|
|
39
|
+
location?: MenuLocation;
|
|
40
|
+
footer?: ReactNode;
|
|
41
|
+
}): React.ReactElement;
|
|
42
|
+
export {};
|
|
@@ -9,6 +9,21 @@ export interface ThemeDefinition {
|
|
|
9
9
|
/** CSS custom properties, e.g. `{ "--flux-accent": "#e11d48", "--flux-bg": "#fff" }`. */
|
|
10
10
|
tokens: Record<string, string>;
|
|
11
11
|
}
|
|
12
|
+
export interface FontDefinition {
|
|
13
|
+
id: string;
|
|
14
|
+
label: string;
|
|
15
|
+
/** CSS font-family stack. */
|
|
16
|
+
stack: string;
|
|
17
|
+
/** Optional @font-face CSS (use data: URLs or the plugin's asset URLs). */
|
|
18
|
+
css?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface SettingsSection {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
icon?: string;
|
|
24
|
+
order?: number;
|
|
25
|
+
component: React.ComponentType;
|
|
26
|
+
}
|
|
12
27
|
export interface SearchResult {
|
|
13
28
|
id: string;
|
|
14
29
|
title: string;
|
|
@@ -21,8 +36,26 @@ export interface SearchProvider {
|
|
|
21
36
|
label: string;
|
|
22
37
|
search: (query: string) => SearchResult[] | Promise<SearchResult[]>;
|
|
23
38
|
}
|
|
24
|
-
/** Declares the extension points used by the built-in theme
|
|
39
|
+
/** Declares the extension points used by the built-in components (theme, fonts, title bar, settings, i18n). Call before `framework.start()`. */
|
|
25
40
|
export declare function declareHostPoints(framework: RendererFramework): void;
|
|
41
|
+
export interface Appearance {
|
|
42
|
+
/** Accent colour (any CSS colour); undefined = the theme's own. */
|
|
43
|
+
accent?: string;
|
|
44
|
+
fontId: string;
|
|
45
|
+
/** Font family typed by the user (used before the chosen font). */
|
|
46
|
+
customFont: string;
|
|
47
|
+
/** Base text size in px (12–20). */
|
|
48
|
+
fontSize: number;
|
|
49
|
+
lineHeight: number;
|
|
50
|
+
density: "comfortable" | "compact";
|
|
51
|
+
translucency: boolean;
|
|
52
|
+
radius: "sharp" | "default" | "round";
|
|
53
|
+
reduceMotion: boolean;
|
|
54
|
+
}
|
|
55
|
+
export declare const DEFAULT_APPEARANCE: Appearance;
|
|
56
|
+
export declare const BUILTIN_FONTS: FontDefinition[];
|
|
57
|
+
/** Turns a font name typed by the user into a safe CSS family list (quotes added, no CSS injection possible). */
|
|
58
|
+
export declare function sanitizeFontFamily(input: string): string;
|
|
26
59
|
interface ThemeCtx {
|
|
27
60
|
mode: ThemeMode;
|
|
28
61
|
setMode(m: ThemeMode): void;
|
|
@@ -31,11 +64,15 @@ interface ThemeCtx {
|
|
|
31
64
|
resolved: "light" | "dark";
|
|
32
65
|
themes: ThemeDefinition[];
|
|
33
66
|
toggle(): void;
|
|
67
|
+
appearance: Appearance;
|
|
68
|
+
setAppearance(patch: Partial<Appearance>): void;
|
|
69
|
+
resetAppearance(): void;
|
|
70
|
+
fonts: FontDefinition[];
|
|
34
71
|
}
|
|
35
72
|
/**
|
|
36
|
-
* Light / dark / system modes
|
|
37
|
-
*
|
|
38
|
-
* colours so the host can
|
|
73
|
+
* Light / dark / system modes, plugin themes, and the user's appearance preferences (accent, font, text size, density,
|
|
74
|
+
* translucency, corner radius, motion). Everything is applied as CSS variables / data attributes on <html> and persisted.
|
|
75
|
+
* `onResolved` reports the resolved colours so the host can colour the native window controls.
|
|
39
76
|
*/
|
|
40
77
|
export declare function ThemeProvider({ children, storageKey, onResolved }: {
|
|
41
78
|
children: ReactNode;
|
|
@@ -48,6 +85,7 @@ export declare function ThemeProvider({ children, storageKey, onResolved }: {
|
|
|
48
85
|
}) => void;
|
|
49
86
|
}): React.JSX.Element;
|
|
50
87
|
export declare function useTheme(): ThemeCtx;
|
|
88
|
+
export declare const useAppearance: () => Pick<ThemeCtx, "appearance" | "setAppearance" | "resetAppearance" | "fonts">;
|
|
51
89
|
/** Sun/moon button; with plugin themes available it also offers a small picker. */
|
|
52
90
|
export declare function ThemeSwitcher(): React.ReactElement;
|
|
53
91
|
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React, { type ButtonHTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
import { type IconName } from "./icons.js";
|
|
3
|
+
/** iOS/macOS-style toggle. */
|
|
4
|
+
export declare function Switch({ checked, onChange, label, disabled }: {
|
|
5
|
+
checked: boolean;
|
|
6
|
+
onChange: (v: boolean) => void;
|
|
7
|
+
label?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
}): React.ReactElement;
|
|
10
|
+
/** Segmented control (like NSSegmentedControl / UISegmentedControl). */
|
|
11
|
+
export declare function Segmented<T extends string>({ value, options, onChange, label }: {
|
|
12
|
+
value: T;
|
|
13
|
+
options: {
|
|
14
|
+
value: T;
|
|
15
|
+
label: ReactNode;
|
|
16
|
+
}[];
|
|
17
|
+
onChange: (v: T) => void;
|
|
18
|
+
label?: string;
|
|
19
|
+
}): React.ReactElement;
|
|
20
|
+
/** Inset grouped list ("Settings" look). */
|
|
21
|
+
export declare function Group({ title, footer, children, className }: {
|
|
22
|
+
title?: ReactNode;
|
|
23
|
+
footer?: ReactNode;
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
className?: string;
|
|
26
|
+
}): React.ReactElement;
|
|
27
|
+
export interface RowProps {
|
|
28
|
+
icon?: IconName | ReactNode;
|
|
29
|
+
iconColor?: string;
|
|
30
|
+
title: ReactNode;
|
|
31
|
+
subtitle?: ReactNode;
|
|
32
|
+
right?: ReactNode;
|
|
33
|
+
chevron?: boolean;
|
|
34
|
+
onClick?: () => void;
|
|
35
|
+
selected?: boolean;
|
|
36
|
+
"data-testid"?: string;
|
|
37
|
+
}
|
|
38
|
+
export declare function Row({ icon, iconColor, title, subtitle, right, chevron, onClick, selected, ...rest }: RowProps): React.ReactElement;
|
|
39
|
+
export declare function Button({ variant, size, children, ...rest }: {
|
|
40
|
+
variant?: "primary" | "secondary" | "destructive" | "plain";
|
|
41
|
+
size?: "sm" | "md" | "lg";
|
|
42
|
+
} & ButtonHTMLAttributes<HTMLButtonElement>): React.ReactElement;
|
package/docs/CUSTOMIZING.md
CHANGED
|
@@ -251,6 +251,62 @@ Authors work in TypeScript (`src/`), but what is **installed and shipped** is on
|
|
|
251
251
|
through the Plugin Manager / registries copy *only* these files — never `src/`, `.ts/.tsx`, sourcemaps, `node_modules`, configs.
|
|
252
252
|
Add `--sign-key` to sign the package (ed25519) and set `trustedKeys` + `requireSignature` in `framework.ts` to refuse the rest.
|
|
253
253
|
|
|
254
|
+
## 9. Settings, languages and fonts
|
|
255
|
+
|
|
256
|
+
**Settings screen** (`/settings/:section`, macOS "System Settings" look). Sections are contributed to the `settings` extension point:
|
|
257
|
+
```ts
|
|
258
|
+
extensions: [{ point: "settings", contribution: { sections: [{ id: "billing", title: "Billing", icon: "info", order: 30, component: BillingSettings }] } }]
|
|
259
|
+
```
|
|
260
|
+
The app ships two plugins that do exactly this: **Appearance** (mode, themes, accent, font, text size, spacing, corners, translucency,
|
|
261
|
+
reduced motion) and **Language**. Remove or replace them freely: they are ordinary plugins in `plugins/`.
|
|
262
|
+
|
|
263
|
+
**Fonts** — users pick a family, type any installed font name, and set size / line spacing; plugins add fonts:
|
|
264
|
+
`extensions: [{ point: "font", contribution: { fonts: [{ id: "brand", label: "Brand", stack: '"Brand Sans", sans-serif', css: "@font-face{…}" }] } }]`.
|
|
265
|
+
In code: `const { appearance, setAppearance } = useAppearance()` (accent, fontId, customFont, fontSize, lineHeight, density, radius, translucency, reduceMotion).
|
|
266
|
+
|
|
267
|
+
**Languages** — `useT()("key", "English fallback")` everywhere; plugins contribute languages and translations to the `i18n` point
|
|
268
|
+
(`{ languages: [{ code: "ar", label: "Arabic", nativeLabel: "العربية", rtl: true }] }`, `{ messages: [{ locale: "ar", messages: {…} }] }`).
|
|
269
|
+
Missing keys fall back to English. Host entries are translated with `menu.<id>` keys (`menu.dashboard`).
|
|
270
|
+
|
|
271
|
+
## 10. Sidebar: icons, collapse, sub-menus
|
|
272
|
+
The sidebar collapses to an icon rail (button at the bottom, `Ctrl/Cmd+B`; state persisted; sub-menus become hover flyouts).
|
|
273
|
+
Plugins add entries and sub-menus:
|
|
274
|
+
```ts
|
|
275
|
+
routes: [{ path: "/reports", component: Reports, meta: { title: "Reports", menu: { label: "Reports", icon: "grid", order: 20 } } },
|
|
276
|
+
{ path: "/reports/sales", component: Sales, meta: { menu: { label: "Sales", parent: "route:/reports" } } }] // parent = menu id
|
|
277
|
+
menus: [{ id: "tools", location: "sidebar", label: "Tools", icon: { d: "M…svg path…" }, children: [{ id: "t1", label: "Sync", path: "/sync" }] }]
|
|
278
|
+
```
|
|
279
|
+
`icon` is a built-in name (`home`, `plug`, `settings`, `palette`…), `{ d: "<svg path>" }` or any React node. Unknown `parent` ids fall back to the top level.
|
|
280
|
+
The shell uses `grid-cols-[var(--flux-sidebar-w,232px)_minmax(0,1fr)]` so the content area follows the collapse.
|
|
281
|
+
|
|
282
|
+
## 11. Images and video — any source
|
|
283
|
+
```tsx
|
|
284
|
+
<Img src="https://…" /> <Img src="data:image/png;base64,…" /> <Img src={rawBase64} /> <Img src={blobUrl} /> <Img src={file /* Blob | File | Uint8Array */} />
|
|
285
|
+
<Img src="C:\\Users\\me\\a.png" /> <Img src="/home/me/a.png" /> <Img src="assets/shot.png" base={pluginAssetBase} />
|
|
286
|
+
```
|
|
287
|
+
Allowed: http(s), `data:image/*`, raw base64 (type detected from the bytes), `blob:`, `file:`/local paths, relative paths (with `base`), Blob/File/ArrayBuffer.
|
|
288
|
+
Rejected: `javascript:` and other schemes. Failing images show a placeholder. Markdown images use the same rules.
|
|
289
|
+
`<VideoPlayer video={parseVideo(url)} />`: **YouTube, Vimeo, Dailymotion, Loom, Streamable** → sandboxed iframe; **mp4 / webm / ogg / mov** (https, or a file inside the plugin such as
|
|
290
|
+
`"video": "./assets/tour.mp4"`) → native HTML5 player with seeking; anything else → a link. Your CSP must list the iframe hosts (the generated `index.html` does).
|
|
291
|
+
|
|
292
|
+
## 12. Build, ship, export
|
|
293
|
+
```bash
|
|
294
|
+
npm run build # app + all plugins (no sourcemaps)
|
|
295
|
+
npm run dist # installer/executable for this OS (electron-builder): npm run dist -- --win --mac --linux
|
|
296
|
+
npm run dist -- --unpacked # just the unpacked app folder (fast check)
|
|
297
|
+
npm run export:web # static website in ./website (renderer + plugin files + bootstrap.json); serve with any static host
|
|
298
|
+
npm run plugins:package # every plugin → release/plugins/*.tgz (runtime files only)
|
|
299
|
+
```
|
|
300
|
+
Bundled plugins are staged **runtime-only** (`.flux-build/plugins`: `plugin.json`, `dist/`, `assets/`, README) — no `src/`, no `.ts`, no `.map`.
|
|
301
|
+
User-installed plugins live in the app's data folder; `deleteAppDataOnUninstall: true` (Windows) removes them with the app. macOS has no uninstaller:
|
|
302
|
+
drag-to-Trash leaves `~/Library/Application Support/<app>`. The static export runs only the browser side (main-process plugin code and IPC are unavailable).
|
|
303
|
+
|
|
304
|
+
## 13. Plugins are normal projects
|
|
305
|
+
`npm create fluxplugin-plugin my-plugin` (or `npx fluxplugin plugin new my-plugin --standalone`) creates a React + TypeScript project with **its own
|
|
306
|
+
`package.json` and libraries**. Install anything (`npm i chart.js react-markdown …`): it is bundled into `dist/`, so the host app needs nothing.
|
|
307
|
+
Only `react`, `react-dom`, `fluxplugin` and `fluxplugin/react` are shared with the host. CSS imported by the plugin or its libraries becomes `dist/renderer.css`
|
|
308
|
+
(auto-listed in the manifest); images and fonts are inlined. `npm run dev:host` rebuilds on save and syncs the runtime files into a running host app, which hot-reloads them.
|
|
309
|
+
|
|
254
310
|
## 9. Plugin stores (marketplace)
|
|
255
311
|
|
|
256
312
|
Where *Plugins → Available* looks is your choice: the default GitHub store, your own catalog (HTTP/GitHub/local), or code of your own.
|
package/docs/STORE.md
CHANGED
|
@@ -86,6 +86,25 @@ jobs:
|
|
|
86
86
|
```
|
|
87
87
|
(The built-in token can create releases but not edit topics; add the topic `fluxplugin-plugin` once in the repo settings.)
|
|
88
88
|
|
|
89
|
+
## B2. A catalog repository (folders + one JSON) — GitHub, any host, or an API
|
|
90
|
+
|
|
91
|
+
Besides the topic store, a store can be **a repository of plugin folders and a single JSON that holds all their metadata**:
|
|
92
|
+
```
|
|
93
|
+
acme/plugin-store
|
|
94
|
+
├─ plugins/
|
|
95
|
+
│ ├─ weather/ plugin.json · dist/ · assets/ · README.md (src/ may be there; it is never listed or downloaded)
|
|
96
|
+
│ └─ notes/ …
|
|
97
|
+
└─ index.json ← generated: id, name, version, description, permissions, icon, screenshots, video, readme, files + sha256 of every file
|
|
98
|
+
```
|
|
99
|
+
```bash
|
|
100
|
+
npx fluxplugin store build . # builds index.json from plugins/*/plugin.json (commit it; run it in CI on every change)
|
|
101
|
+
```
|
|
102
|
+
Point the app at it: `{ "type": "github", "repo": "acme/plugin-store" }` (reads `index.json` from the repo), **or** any link that returns the JSON:
|
|
103
|
+
`{ "type": "json", "url": "https://api.acme.com/plugins" }` / `{ "type": "http", "url": "https://acme.github.io/store/index.json" }`.
|
|
104
|
+
Entries are either packages (`url` + `sha256`) or folders (`folder` + `files` + `hashes`); relative URLs resolve against the JSON. The app asks the store first, then
|
|
105
|
+
compares each entry with what is installed locally (*Installed / Update / Get*). Every downloaded file is verified against its hash.
|
|
106
|
+
`fluxplugin init my-app --store github:acme/plugin-store` writes this into `fluxplugin.config.json`.
|
|
107
|
+
|
|
89
108
|
## C. Install from the store
|
|
90
109
|
|
|
91
110
|
* **In an app**: *Plugins → Available* lists plugins with their permissions; **Install**. Enabled automatically (or after the user approves permissions when your app uses `requireApproval`).
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fluxplugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Plugin architecture for Electron + React + TypeScript. Build apps that others can extend: routes, menus, commands, UI slots, IPC, hooks, permissions — with a CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|