fluxplugin 1.0.2 → 1.0.3

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/dist/cli.js CHANGED
@@ -1441,8 +1441,8 @@ var HOST_TAILWIND_FILES = {
1441
1441
  </html>
1442
1442
  `,
1443
1443
  "src/app/renderer/pages/DashboardPage.tsx": 'import { PluginSlot, useExtensionItems, usePluginFramework } from "fluxplugin/react";\nimport { EXTENSION_POINTS, SLOTS } from "../../shared/constants";\nimport type { ToolbarAction } from "../../shared/types";\nimport { useCreateItem } from "../hooks/useCreateItem";\n\nexport function DashboardPage() {\n const framework = usePluginFramework();\n const actions = useExtensionItems<ToolbarAction>(EXTENSION_POINTS.dashboard, "toolbar");\n const { create, result } = useCreateItem();\n return (\n <div>\n <h2>Dashboard</h2>\n <PluginSlot name={SLOTS.dashboardBefore} />\n <p className="text-xs text-muted">These buttons are contributed by plugins through a host-declared extension point.</p>\n <div className="my-3 flex flex-wrap gap-2" data-testid="toolbar">\n {actions.map((a) => (\n <button key={a.id} className="cursor-pointer rounded-lg border border-border bg-bg px-3 py-1.5 hover:bg-surface" onClick={() => a.command && void framework.manager.commands.execute(a.command)}>\n {a.label}\n </button>\n ))}\n </div>\n <div className="my-3 flex flex-wrap gap-2">\n <button className="cursor-pointer rounded-lg border border-border bg-bg px-3 py-1.5 hover:bg-surface" onClick={() => void create(" Ada ")}>Create item \u201C Ada \u201D (plugin hooks may rewrite it)</button>\n <button className="cursor-pointer rounded-lg border border-border bg-bg px-3 py-1.5 hover:bg-surface" onClick={() => void create(" ")}>Create empty item (a plugin may veto)</button>\n </div>\n <div className="my-3 flex flex-wrap gap-2">\n <button className="cursor-pointer rounded-lg border border-border bg-bg px-3 py-1.5 hover:bg-surface" data-testid="notify" onClick={() => framework.manager.notifications.push({ title: "Hello from the host", body: "Plugins can do this too.", level: "success" })}>Show a notification</button>\n </div>\n <pre data-testid="hook-result" className="text-xs text-muted">{result}</pre>\n <PluginSlot name={SLOTS.dashboardAfter} />\n </div>\n );\n}\n',
1444
- "src/app/renderer/shell/AppShell.tsx": 'import { Breadcrumbs, CommandPalette, PluginRoutes, PluginSlot, TitleBar, useKeybindings } from "fluxplugin/react";\nimport { APP_NAME, SLOTS } from "../../shared/constants";\nimport { Sidebar } from "./Sidebar";\n\n/** Same structure as the default template, styled with Tailwind utilities. */\nexport function AppShell() {\n useKeybindings();\n return (\n <div className="app grid min-h-screen grid-rows-[40px_1fr]">\n <TitleBar title={APP_NAME} logo={<PluginSlot name={SLOTS.brandBadges} />} searchPlaceholder="Search commands, pages\u2026" />\n <div className="body grid min-h-0 grid-cols-[220px_1fr]">\n <Sidebar />\n <main className="main max-w-3xl overflow-auto p-6">\n <Breadcrumbs />\n <PluginRoutes />\n </main>\n </div>\n <CommandPalette />\n </div>\n );\n}\n',
1445
- "src/app/renderer/shell/Sidebar.tsx": 'import { Link, useMenu } from "fluxplugin/react";\n\nconst link = "block rounded-lg px-2.5 py-1.5 no-underline text-fg data-[active]:bg-accent data-[active]:text-white";\n\nexport function Sidebar() {\n const { items, run } = useMenu("sidebar");\n return (\n <aside className="side border-r border-border bg-surface p-4">\n <Link to="/" className={link}>Dashboard</Link>\n {items.map((i) => (\n <a key={i.id} className={link} href={"#" + i.path} onClick={(e) => (e.preventDefault(), run(i))}>{i.label}</a>\n ))}\n <Link to="/plugins" className={link}>Plugins</Link>\n <p className="mt-3 text-xs text-muted">Ctrl/Cmd+K: commands</p>\n </aside>\n );\n}\n',
1444
+ "src/app/renderer/shell/AppShell.tsx": 'import {\n Breadcrumbs,\n CommandPalette,\n PluginRoutes,\n PluginSlot,\n TitleBar,\n useKeybindings,\n} from "fluxplugin/react";\n\nimport { APP_NAME, SLOTS } from "../../shared/constants";\nimport { Sidebar } from "./Sidebar";\n\n/** App shell with a fixed sidebar and independently scrollable content. */\nexport function AppShell() {\n useKeybindings();\n\n return (\n <div className="app grid h-screen grid-rows-[40px_minmax(0,1fr)] overflow-hidden">\n {/* Title bar */}\n <header className="z-50 min-w-0">\n <TitleBar\n title={APP_NAME}\n logo={<PluginSlot name={SLOTS.brandBadges} />}\n searchPlaceholder="Search commands, pages\u2026"\n />\n </header>\n\n {/* Application body */}\n <div className="body grid min-h-0 min-w-0 grid-cols-[220px_minmax(0,1fr)] overflow-hidden">\n {/* Fixed sidebar: never scrolls with the main content */}\n <aside className="h-full min-h-0 overflow-hidden border-r border-transparent">\n <Sidebar />\n </aside>\n\n {/* Only this area scrolls */}\n <main className="main min-h-0 min-w-0 overflow-y-auto overflow-x-hidden p-6">\n <div className="mx-auto max-w-3xl">\n <Breadcrumbs />\n\n <PluginRoutes />\n </div>\n </main>\n </div>\n\n {/* Command palette */}\n <CommandPalette />\n </div>\n );\n}\n',
1445
+ "src/app/renderer/shell/Sidebar.tsx": 'import { Link, useMenu } from "fluxplugin/react";\nimport { useEffect, useState } from "react";\n\nconst link =\n "block rounded-lg px-2.5 py-1.5 no-underline text-fg data-[active]:bg-accent data-[active]:text-white";\n\nexport function Sidebar() {\n const { items, run } = useMenu("sidebar");\n const [activePath, setActivePath] = useState(() => {\n const hash = window.location.hash.slice(1);\n return hash || window.location.pathname;\n });\n\n useEffect(() => {\n const updateActivePath = () => {\n setActivePath(window.location.hash.slice(1) || window.location.pathname);\n };\n\n window.addEventListener("hashchange", updateActivePath);\n window.addEventListener("popstate", updateActivePath);\n\n return () => {\n window.removeEventListener("hashchange", updateActivePath);\n window.removeEventListener("popstate", updateActivePath);\n };\n }, []);\n\n const isActive = (path: string) => {\n const normalize = (value: string) =>\n value.replace(/^#/, "").replace(/\\/+$/, "") || "/";\n\n return normalize(activePath) === normalize(path);\n };\n\n return (\n <aside className="side h-full overflow-hidden border-r border-border bg-surface p-4">\n <nav className="flex flex-col gap-1">\n <Link\n to="/"\n data-active={isActive("/") ? "" : undefined}\n aria-current={isActive("/") ? "page" : undefined}\n className={link}\n >\n Dashboard\n </Link>\n\n {items.map((i) => (\n <a\n key={i.id}\n className={link}\n data-active={isActive(i.path!) ? "" : undefined}\n aria-current={isActive(i.path!) ? "page" : undefined}\n href={"#" + i.path}\n onClick={(e) => {\n e.preventDefault();\n setActivePath(i.path!);\n run(i);\n }}\n >\n {i.label}\n </a>\n ))}\n\n <Link\n to="/plugins"\n data-active={isActive("/plugins") ? "" : undefined}\n aria-current={isActive("/plugins") ? "page" : undefined}\n className={link}\n >\n Plugins\n </Link>\n </nav>\n\n <p className="mt-3 text-xs text-muted">Ctrl/Cmd+K: commands</p>\n </aside>\n );\n}\n',
1446
1446
  "src/app/renderer/styles/app.css": "body { margin: 0; font: 14px system-ui; background: var(--flux-bg); color: var(--flux-fg); }\n",
1447
1447
  "src/app/renderer/styles/tailwind.css": '/* Tailwind v4, compiled by `fluxplugin dev|build` to dist/tailwind.css.\n Colors come from the theme variables, so Tailwind utilities follow light / dark / plugin themes automatically:\n use bg-surface, text-fg, border-border, text-muted, bg-accent \u2026 */\n@import "tailwindcss";\n\n/* Plugins are shipped as built JS: scan their dist so the classes they use are generated. Plugins that users install later\n should ship their own precompiled CSS (fluxplugin plugin new my-plugin --tailwindcss). */\n@source "../../../../plugins/*/dist";\n\n@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));\n\n@theme inline {\n --color-bg: var(--flux-bg);\n --color-fg: var(--flux-fg);\n --color-surface: var(--flux-surface);\n --color-border: var(--flux-border);\n --color-accent: var(--flux-accent);\n --color-muted: var(--flux-muted);\n}\n'
1448
1448
  };
@@ -377,8 +377,8 @@ var HOST_TAILWIND_FILES = {
377
377
  </html>
378
378
  `,
379
379
  "src/app/renderer/pages/DashboardPage.tsx": 'import { PluginSlot, useExtensionItems, usePluginFramework } from "fluxplugin/react";\nimport { EXTENSION_POINTS, SLOTS } from "../../shared/constants";\nimport type { ToolbarAction } from "../../shared/types";\nimport { useCreateItem } from "../hooks/useCreateItem";\n\nexport function DashboardPage() {\n const framework = usePluginFramework();\n const actions = useExtensionItems<ToolbarAction>(EXTENSION_POINTS.dashboard, "toolbar");\n const { create, result } = useCreateItem();\n return (\n <div>\n <h2>Dashboard</h2>\n <PluginSlot name={SLOTS.dashboardBefore} />\n <p className="text-xs text-muted">These buttons are contributed by plugins through a host-declared extension point.</p>\n <div className="my-3 flex flex-wrap gap-2" data-testid="toolbar">\n {actions.map((a) => (\n <button key={a.id} className="cursor-pointer rounded-lg border border-border bg-bg px-3 py-1.5 hover:bg-surface" onClick={() => a.command && void framework.manager.commands.execute(a.command)}>\n {a.label}\n </button>\n ))}\n </div>\n <div className="my-3 flex flex-wrap gap-2">\n <button className="cursor-pointer rounded-lg border border-border bg-bg px-3 py-1.5 hover:bg-surface" onClick={() => void create(" Ada ")}>Create item \u201C Ada \u201D (plugin hooks may rewrite it)</button>\n <button className="cursor-pointer rounded-lg border border-border bg-bg px-3 py-1.5 hover:bg-surface" onClick={() => void create(" ")}>Create empty item (a plugin may veto)</button>\n </div>\n <div className="my-3 flex flex-wrap gap-2">\n <button className="cursor-pointer rounded-lg border border-border bg-bg px-3 py-1.5 hover:bg-surface" data-testid="notify" onClick={() => framework.manager.notifications.push({ title: "Hello from the host", body: "Plugins can do this too.", level: "success" })}>Show a notification</button>\n </div>\n <pre data-testid="hook-result" className="text-xs text-muted">{result}</pre>\n <PluginSlot name={SLOTS.dashboardAfter} />\n </div>\n );\n}\n',
380
- "src/app/renderer/shell/AppShell.tsx": 'import { Breadcrumbs, CommandPalette, PluginRoutes, PluginSlot, TitleBar, useKeybindings } from "fluxplugin/react";\nimport { APP_NAME, SLOTS } from "../../shared/constants";\nimport { Sidebar } from "./Sidebar";\n\n/** Same structure as the default template, styled with Tailwind utilities. */\nexport function AppShell() {\n useKeybindings();\n return (\n <div className="app grid min-h-screen grid-rows-[40px_1fr]">\n <TitleBar title={APP_NAME} logo={<PluginSlot name={SLOTS.brandBadges} />} searchPlaceholder="Search commands, pages\u2026" />\n <div className="body grid min-h-0 grid-cols-[220px_1fr]">\n <Sidebar />\n <main className="main max-w-3xl overflow-auto p-6">\n <Breadcrumbs />\n <PluginRoutes />\n </main>\n </div>\n <CommandPalette />\n </div>\n );\n}\n',
381
- "src/app/renderer/shell/Sidebar.tsx": 'import { Link, useMenu } from "fluxplugin/react";\n\nconst link = "block rounded-lg px-2.5 py-1.5 no-underline text-fg data-[active]:bg-accent data-[active]:text-white";\n\nexport function Sidebar() {\n const { items, run } = useMenu("sidebar");\n return (\n <aside className="side border-r border-border bg-surface p-4">\n <Link to="/" className={link}>Dashboard</Link>\n {items.map((i) => (\n <a key={i.id} className={link} href={"#" + i.path} onClick={(e) => (e.preventDefault(), run(i))}>{i.label}</a>\n ))}\n <Link to="/plugins" className={link}>Plugins</Link>\n <p className="mt-3 text-xs text-muted">Ctrl/Cmd+K: commands</p>\n </aside>\n );\n}\n',
380
+ "src/app/renderer/shell/AppShell.tsx": 'import {\n Breadcrumbs,\n CommandPalette,\n PluginRoutes,\n PluginSlot,\n TitleBar,\n useKeybindings,\n} from "fluxplugin/react";\n\nimport { APP_NAME, SLOTS } from "../../shared/constants";\nimport { Sidebar } from "./Sidebar";\n\n/** App shell with a fixed sidebar and independently scrollable content. */\nexport function AppShell() {\n useKeybindings();\n\n return (\n <div className="app grid h-screen grid-rows-[40px_minmax(0,1fr)] overflow-hidden">\n {/* Title bar */}\n <header className="z-50 min-w-0">\n <TitleBar\n title={APP_NAME}\n logo={<PluginSlot name={SLOTS.brandBadges} />}\n searchPlaceholder="Search commands, pages\u2026"\n />\n </header>\n\n {/* Application body */}\n <div className="body grid min-h-0 min-w-0 grid-cols-[220px_minmax(0,1fr)] overflow-hidden">\n {/* Fixed sidebar: never scrolls with the main content */}\n <aside className="h-full min-h-0 overflow-hidden border-r border-transparent">\n <Sidebar />\n </aside>\n\n {/* Only this area scrolls */}\n <main className="main min-h-0 min-w-0 overflow-y-auto overflow-x-hidden p-6">\n <div className="mx-auto max-w-3xl">\n <Breadcrumbs />\n\n <PluginRoutes />\n </div>\n </main>\n </div>\n\n {/* Command palette */}\n <CommandPalette />\n </div>\n );\n}\n',
381
+ "src/app/renderer/shell/Sidebar.tsx": 'import { Link, useMenu } from "fluxplugin/react";\nimport { useEffect, useState } from "react";\n\nconst link =\n "block rounded-lg px-2.5 py-1.5 no-underline text-fg data-[active]:bg-accent data-[active]:text-white";\n\nexport function Sidebar() {\n const { items, run } = useMenu("sidebar");\n const [activePath, setActivePath] = useState(() => {\n const hash = window.location.hash.slice(1);\n return hash || window.location.pathname;\n });\n\n useEffect(() => {\n const updateActivePath = () => {\n setActivePath(window.location.hash.slice(1) || window.location.pathname);\n };\n\n window.addEventListener("hashchange", updateActivePath);\n window.addEventListener("popstate", updateActivePath);\n\n return () => {\n window.removeEventListener("hashchange", updateActivePath);\n window.removeEventListener("popstate", updateActivePath);\n };\n }, []);\n\n const isActive = (path: string) => {\n const normalize = (value: string) =>\n value.replace(/^#/, "").replace(/\\/+$/, "") || "/";\n\n return normalize(activePath) === normalize(path);\n };\n\n return (\n <aside className="side h-full overflow-hidden border-r border-border bg-surface p-4">\n <nav className="flex flex-col gap-1">\n <Link\n to="/"\n data-active={isActive("/") ? "" : undefined}\n aria-current={isActive("/") ? "page" : undefined}\n className={link}\n >\n Dashboard\n </Link>\n\n {items.map((i) => (\n <a\n key={i.id}\n className={link}\n data-active={isActive(i.path!) ? "" : undefined}\n aria-current={isActive(i.path!) ? "page" : undefined}\n href={"#" + i.path}\n onClick={(e) => {\n e.preventDefault();\n setActivePath(i.path!);\n run(i);\n }}\n >\n {i.label}\n </a>\n ))}\n\n <Link\n to="/plugins"\n data-active={isActive("/plugins") ? "" : undefined}\n aria-current={isActive("/plugins") ? "page" : undefined}\n className={link}\n >\n Plugins\n </Link>\n </nav>\n\n <p className="mt-3 text-xs text-muted">Ctrl/Cmd+K: commands</p>\n </aside>\n );\n}\n',
382
382
  "src/app/renderer/styles/app.css": "body { margin: 0; font: 14px system-ui; background: var(--flux-bg); color: var(--flux-fg); }\n",
383
383
  "src/app/renderer/styles/tailwind.css": '/* Tailwind v4, compiled by `fluxplugin dev|build` to dist/tailwind.css.\n Colors come from the theme variables, so Tailwind utilities follow light / dark / plugin themes automatically:\n use bg-surface, text-fg, border-border, text-muted, bg-accent \u2026 */\n@import "tailwindcss";\n\n/* Plugins are shipped as built JS: scan their dist so the classes they use are generated. Plugins that users install later\n should ship their own precompiled CSS (fluxplugin plugin new my-plugin --tailwindcss). */\n@source "../../../../plugins/*/dist";\n\n@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));\n\n@theme inline {\n --color-bg: var(--flux-bg);\n --color-fg: var(--flux-fg);\n --color-surface: var(--flux-surface);\n --color-border: var(--flux-border);\n --color-accent: var(--flux-accent);\n --color-muted: var(--flux-muted);\n}\n'
384
384
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxplugin",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
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": [