ssovee-os-web-ui 0.1.26 → 0.1.29

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 CHANGED
@@ -123,6 +123,34 @@ export function SettingsWindow() {
123
123
  ```
124
124
  {% endraw %}
125
125
 
126
+ ## Plugin playground
127
+
128
+ ```tsx
129
+ import { PluginPlayground, Typography, Button } from "ssovee-os-web-ui";
130
+
131
+ const pluginMetadata = {
132
+ title: "Plugin Preview",
133
+ defaultWidth: 720,
134
+ defaultHeight: 520,
135
+ windowVariant: "with-side-menu",
136
+ };
137
+
138
+ export function PluginPreview() {
139
+ return (
140
+ <PluginPlayground
141
+ metadata={pluginMetadata}
142
+ onClose={() => console.log("Playground closed")}
143
+ onMinimize={(isMinimized) => console.log("Minimized:", isMinimized)}
144
+ >
145
+ <div>
146
+ <Typography variant="h6">Plugin Content</Typography>
147
+ <Button variant="primary">Run</Button>
148
+ </div>
149
+ </PluginPlayground>
150
+ );
151
+ }
152
+ ```
153
+
126
154
  ## Hooks
127
155
 
128
156
  Hooks are also available through the SDK-style entrypoint:
package/dist/index.cjs CHANGED
@@ -2036,6 +2036,201 @@ var Tabs = ({
2036
2036
  ] });
2037
2037
  };
2038
2038
  var Tabs_default = Tabs;
2039
+ var DEFAULT_BACKGROUND = "/wallpaper/Wallpaper3.webp";
2040
+ var DEFAULT_MOCK_USER = {
2041
+ id: "dev-user",
2042
+ name: "Plugin Developer",
2043
+ email: "dev@local.test"
2044
+ };
2045
+ var DEFAULT_TOKEN = "playground-dev-token";
2046
+ function renderPluginChild(child, app, api, reloadKey) {
2047
+ if (!child) return null;
2048
+ if (typeof child === "function") {
2049
+ const renderFn = child;
2050
+ return renderFn({ app, api });
2051
+ }
2052
+ return React22.Children.map(child, (node, index) => {
2053
+ if (!React22.isValidElement(node)) {
2054
+ return node;
2055
+ }
2056
+ return React22.cloneElement(
2057
+ node,
2058
+ {
2059
+ key: `${reloadKey}-${index}`,
2060
+ app,
2061
+ api
2062
+ }
2063
+ );
2064
+ });
2065
+ }
2066
+ function getDefaultSize(metadata) {
2067
+ const manifestWindowSize = metadata.windowSize?.windowSize;
2068
+ const minWidth = metadata.minWidth ?? 520;
2069
+ const minHeight = metadata.minHeight ?? 360;
2070
+ return {
2071
+ width: Math.max(
2072
+ manifestWindowSize?.width ?? metadata.defaultWidth ?? 960,
2073
+ minWidth
2074
+ ),
2075
+ height: Math.max(
2076
+ manifestWindowSize?.height ?? metadata.defaultHeight ?? 680,
2077
+ minHeight
2078
+ )
2079
+ };
2080
+ }
2081
+ function PluginPlayground({
2082
+ children,
2083
+ metadata,
2084
+ backgroundImage,
2085
+ onClose,
2086
+ onMinimize
2087
+ }) {
2088
+ const [mockUser] = React22.useState(DEFAULT_MOCK_USER);
2089
+ const [mockToken] = React22.useState(DEFAULT_TOKEN);
2090
+ const [isMinimized, setIsMinimized] = React22.useState(false);
2091
+ const [isClosed, setIsClosed] = React22.useState(false);
2092
+ const [isActive, setIsActive] = React22.useState(true);
2093
+ const [isNightMode, setIsNightMode] = React22.useState(true);
2094
+ React22.useEffect(() => {
2095
+ if (typeof window === "undefined") return;
2096
+ const storedTheme = window.localStorage.getItem("plugin-playground-theme");
2097
+ const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
2098
+ const shouldUseDark = storedTheme ? storedTheme === "dark" : prefersDark;
2099
+ setIsNightMode(shouldUseDark);
2100
+ }, []);
2101
+ React22.useEffect(() => {
2102
+ if (typeof document === "undefined") return;
2103
+ document.documentElement.classList.toggle("dark", isNightMode);
2104
+ window.localStorage.setItem(
2105
+ "plugin-playground-theme",
2106
+ isNightMode ? "dark" : "light"
2107
+ );
2108
+ }, [isNightMode]);
2109
+ const title = metadata.title ?? metadata.name ?? "Plugin Playground";
2110
+ const slug = metadata.slug ?? "local-plugin";
2111
+ const defaultSize = React22.useMemo(() => getDefaultSize(metadata), [metadata]);
2112
+ const canvasBackground = backgroundImage ?? metadata.backgroundImage ?? DEFAULT_BACKGROUND;
2113
+ const isWindowResizable = metadata.windowSize?.isAppWindowResizing ?? metadata.isResizing ?? true;
2114
+ const minimizeWindow = React22.useCallback(() => {
2115
+ setIsActive(false);
2116
+ setIsMinimized(true);
2117
+ setIsClosed(false);
2118
+ onMinimize?.(true);
2119
+ }, [onMinimize]);
2120
+ const closeWindow = React22.useCallback(() => {
2121
+ setIsActive(false);
2122
+ setIsClosed(true);
2123
+ setIsMinimized(false);
2124
+ onClose?.();
2125
+ }, [onClose]);
2126
+ const restoreWindow = React22.useCallback(() => {
2127
+ setIsActive(true);
2128
+ setIsClosed(false);
2129
+ setIsMinimized(false);
2130
+ onMinimize?.(false);
2131
+ }, [onMinimize]);
2132
+ const mockApi = React22.useMemo(
2133
+ () => ({
2134
+ showToast: (message, type) => {
2135
+ console.info(`[playground:${type}] ${message}`);
2136
+ },
2137
+ openApp: (app) => {
2138
+ console.info(`[playground:openApp] ${app.slug}`);
2139
+ },
2140
+ getUser: async () => {
2141
+ return mockUser;
2142
+ },
2143
+ getToken: async () => {
2144
+ return mockToken;
2145
+ }
2146
+ }),
2147
+ [mockToken, mockUser]
2148
+ );
2149
+ const appProps = React22.useMemo(
2150
+ () => ({
2151
+ name: title,
2152
+ slug,
2153
+ icon: metadata.icon,
2154
+ isActive,
2155
+ isMinimize: isMinimized,
2156
+ componentKey: "plugin-playground",
2157
+ windowSize: {
2158
+ windowSize: defaultSize,
2159
+ isAppWindowResizing: isWindowResizable
2160
+ },
2161
+ metadata: {
2162
+ title,
2163
+ description: metadata.description,
2164
+ ogImage: metadata.icon,
2165
+ keywords: metadata.keywords?.join(", "),
2166
+ author: metadata.author,
2167
+ siteName: "Plugin Playground"
2168
+ },
2169
+ callback: (value) => {
2170
+ if (value === "MINIMIZE_APP") {
2171
+ minimizeWindow();
2172
+ return;
2173
+ }
2174
+ if (value === "CLOSE_APP") {
2175
+ closeWindow();
2176
+ return;
2177
+ }
2178
+ if (value === "CLICKED") {
2179
+ setIsActive(true);
2180
+ }
2181
+ }
2182
+ }),
2183
+ [
2184
+ closeWindow,
2185
+ defaultSize,
2186
+ isActive,
2187
+ isMinimized,
2188
+ metadata.author,
2189
+ metadata.description,
2190
+ metadata.icon,
2191
+ metadata.keywords,
2192
+ minimizeWindow,
2193
+ isWindowResizable,
2194
+ slug,
2195
+ title
2196
+ ]
2197
+ );
2198
+ const injectedPlugin = renderPluginChild(
2199
+ children,
2200
+ appProps,
2201
+ mockApi,
2202
+ Number(isClosed)
2203
+ );
2204
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2205
+ "div",
2206
+ {
2207
+ className: "relative h-screen w-screen overflow-hidden bg-cover bg-center bg-no-repeat text-white transition-colors duration-200 dark:text-slate-100",
2208
+ style: { backgroundImage: `url(${canvasBackground})` },
2209
+ children: [
2210
+ /* @__PURE__ */ jsxRuntime.jsx(
2211
+ "button",
2212
+ {
2213
+ "aria-pressed": isNightMode,
2214
+ className: "absolute left-4 top-4 z-40 rounded-full border border-white/30 bg-white/75 px-3 py-2 text-xs font-medium text-slate-900 shadow-lg shadow-black/20 backdrop-blur-md transition-all duration-200 dark:border-slate-700/60 dark:bg-slate-900/70 dark:text-slate-100 dark:shadow-slate-950/40",
2215
+ onClick: () => setIsNightMode((prev) => !prev),
2216
+ type: "button",
2217
+ children: isNightMode ? "\u2600\uFE0F Light Mood" : "\u{1F319} Night Mood"
2218
+ }
2219
+ ),
2220
+ !isClosed ? injectedPlugin : null,
2221
+ isClosed || isMinimized ? /* @__PURE__ */ jsxRuntime.jsx(
2222
+ "button",
2223
+ {
2224
+ className: "absolute bottom-6 left-1/2 z-40 -translate-x-1/2 rounded-full border border-white/30 bg-black/55 px-4 py-2 text-xs text-white backdrop-blur-md transition-colors duration-200 dark:border-white/20 dark:bg-slate-950/70 dark:text-slate-100",
2225
+ onClick: restoreWindow,
2226
+ type: "button",
2227
+ children: isClosed ? `Reopen ${title}` : `Restore ${title}`
2228
+ }
2229
+ ) : null
2230
+ ]
2231
+ }
2232
+ );
2233
+ }
2039
2234
  var SIDEBAR_WIDTH = 200;
2040
2235
  var initialState = {
2041
2236
  position: { x: 0, y: 40 },
@@ -2928,6 +3123,7 @@ exports.Input = Input_default;
2928
3123
  exports.Loading = Loading_default;
2929
3124
  exports.Modal = Modal_default;
2930
3125
  exports.Pagination = Pagination_default;
3126
+ exports.PluginPlayground = PluginPlayground;
2931
3127
  exports.Progress = Progress_default;
2932
3128
  exports.Radio = Radio_default;
2933
3129
  exports.SearchInput = SearchInput_default;