ssovee-os-web-ui 0.1.25 → 0.1.28
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 +34 -6
- package/dist/index.cjs +196 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +106 -0
- package/dist/index.d.cts +90 -10
- package/dist/index.d.ts +90 -10
- package/dist/index.js +197 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -6,18 +6,13 @@ Reusable React UI components for building shared desktop-style web experiences.
|
|
|
6
6
|
|
|
7
7
|
This package provides a shared set of React UI components for SSOVEE applications. It includes themed UI primitives, hooks, and desktop-style window wrappers for app-like layouts.
|
|
8
8
|
|
|
9
|
-
## What’s new in v0.1.24
|
|
10
|
-
|
|
11
|
-
- Added window container components for app-style layouts
|
|
12
|
-
- Documented usage examples for both window variants
|
|
13
|
-
- Improved package exports for easier consumption
|
|
14
|
-
|
|
15
9
|
## Features
|
|
16
10
|
|
|
17
11
|
- Ready-to-use React UI components
|
|
18
12
|
- Packaged theme stylesheet for shared visual tokens
|
|
19
13
|
- TypeScript support for the main public components
|
|
20
14
|
- Window components for desktop-style app shells
|
|
15
|
+
- Plugin playground support for local plugin testing
|
|
21
16
|
|
|
22
17
|
## Install
|
|
23
18
|
|
|
@@ -64,6 +59,7 @@ export function Example() {
|
|
|
64
59
|
|
|
65
60
|
### Simple window
|
|
66
61
|
|
|
62
|
+
{% raw %}
|
|
67
63
|
```tsx
|
|
68
64
|
import { WindowWithoutSideMenu, Typography } from "ssovee-os-web-ui";
|
|
69
65
|
|
|
@@ -86,9 +82,11 @@ export function NotesWindow() {
|
|
|
86
82
|
);
|
|
87
83
|
}
|
|
88
84
|
```
|
|
85
|
+
{% endraw %}
|
|
89
86
|
|
|
90
87
|
### Window with sidebar menu
|
|
91
88
|
|
|
89
|
+
{% raw %}
|
|
92
90
|
```tsx
|
|
93
91
|
import { WindowWithSideMenu, Typography, Button } from "ssovee-os-web-ui";
|
|
94
92
|
|
|
@@ -123,6 +121,35 @@ export function SettingsWindow() {
|
|
|
123
121
|
);
|
|
124
122
|
}
|
|
125
123
|
```
|
|
124
|
+
{% endraw %}
|
|
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
|
+
```
|
|
126
153
|
|
|
127
154
|
## Hooks
|
|
128
155
|
|
|
@@ -163,6 +190,7 @@ The package exports these components and types for external use:
|
|
|
163
190
|
- `Tabs`
|
|
164
191
|
- `Typography`
|
|
165
192
|
- `ImageWithPlaceholder`
|
|
193
|
+
- `PluginPlayground`
|
|
166
194
|
- `WindowWithSideMenu`
|
|
167
195
|
- `WindowWithoutSideMenu`
|
|
168
196
|
- `Table` and its subcomponents
|
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;
|