@owdproject/react-theme-win95 4.0.0-alpha.3 → 4.0.0-alpha.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/package.json +2 -3
- package/src/DesktopShell.tsx +10 -18
- package/src/applications.ts +25 -0
- package/src/components/DesktopWindow.tsx +7 -6
- package/src/components/SystemBar.tsx +2 -2
- package/src/index.ts +0 -1
- package/src/theme.css +4 -0
- package/src/types.ts +10 -6
- package/playground/index.html +0 -12
- package/playground/src/App.tsx +0 -30
- package/playground/src/main.tsx +0 -14
- package/playground/tsconfig.json +0 -15
- package/playground/vite.config.ts +0 -43
- package/src/apps/About.tsx +0 -27
- package/src/apps/Notepad.tsx +0 -24
- package/src/apps/index.tsx +0 -24
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owdproject/react-theme-win95",
|
|
3
|
-
"version": "4.0.0-alpha.
|
|
3
|
+
"version": "4.0.0-alpha.4",
|
|
4
4
|
"description": "Windows 95 theme for Open Web Desktop built with React, @owdproject/core and @owdproject/react.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -21,8 +21,7 @@
|
|
|
21
21
|
"./package.json": "./package.json"
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
|
-
"src"
|
|
25
|
-
"playground"
|
|
24
|
+
"src"
|
|
26
25
|
],
|
|
27
26
|
"scripts": {
|
|
28
27
|
"dev": "vite --config playground/vite.config.ts",
|
package/src/DesktopShell.tsx
CHANGED
|
@@ -6,15 +6,16 @@ import { DesktopRuntimeContext } from './desktop-context'
|
|
|
6
6
|
import { DesktopWindow } from './components/DesktopWindow'
|
|
7
7
|
import { SystemBar } from './components/SystemBar'
|
|
8
8
|
import { StartMenuPanel } from './components/StartMenuPanel'
|
|
9
|
-
import {
|
|
9
|
+
import { registerDesktopApps } from './applications'
|
|
10
10
|
import type { DesktopApp } from './types'
|
|
11
11
|
|
|
12
12
|
const SYSTEMBAR_HEIGHT = 28
|
|
13
|
+
const EMPTY_APPS: DesktopApp[] = []
|
|
13
14
|
|
|
14
15
|
export interface DesktopShellProps {
|
|
15
16
|
/** A `@owdproject/core` Desktop instance. */
|
|
16
17
|
desktop: Desktop
|
|
17
|
-
/**
|
|
18
|
+
/** Menu entries; components/configs register with the engine on mount. */
|
|
18
19
|
apps?: DesktopApp[]
|
|
19
20
|
/** Centered desktop watermark text. */
|
|
20
21
|
watermark?: ReactNode
|
|
@@ -25,12 +26,15 @@ export interface DesktopShellProps {
|
|
|
25
26
|
* Composes `@owdproject/react-kit-react95` chrome with `@owdproject/react` hooks
|
|
26
27
|
* and the headless `@owdproject/core` engine.
|
|
27
28
|
*/
|
|
28
|
-
export function DesktopShell({ desktop, apps =
|
|
29
|
+
export function DesktopShell({ desktop, apps = EMPTY_APPS, watermark = 'Open Web Desktop' }: DesktopShellProps) {
|
|
29
30
|
const rootRef = useRef<HTMLDivElement>(null)
|
|
30
31
|
const [startOpen, setStartOpen] = useState(false)
|
|
31
|
-
const idRef = useRef(0)
|
|
32
32
|
const { windows } = useWindowManager(desktop.windows)
|
|
33
33
|
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
registerDesktopApps(desktop, apps)
|
|
36
|
+
}, [desktop, apps])
|
|
37
|
+
|
|
34
38
|
const getWorkarea = useCallback(() => {
|
|
35
39
|
const el = rootRef.current
|
|
36
40
|
const width = el?.clientWidth ?? (typeof window !== 'undefined' ? window.innerWidth : 1024)
|
|
@@ -48,19 +52,7 @@ export function DesktopShell({ desktop, apps = builtinApps, watermark = 'Open We
|
|
|
48
52
|
const runtime = useMemo(() => ({ desktop, getWorkarea }), [desktop, getWorkarea])
|
|
49
53
|
|
|
50
54
|
const launch = (app: DesktopApp) => {
|
|
51
|
-
|
|
52
|
-
const count = windows.length
|
|
53
|
-
const size = app.defaultSize ?? { width: 480, height: 340 }
|
|
54
|
-
desktop.windows.createWindow({
|
|
55
|
-
id: `${app.id}-${idRef.current}`,
|
|
56
|
-
model: app.id,
|
|
57
|
-
applicationId: app.id,
|
|
58
|
-
config: {
|
|
59
|
-
title: app.title,
|
|
60
|
-
size,
|
|
61
|
-
position: { x: 48 + (count % 7) * 26, y: 40 + (count % 7) * 26 },
|
|
62
|
-
},
|
|
63
|
-
})
|
|
55
|
+
void desktop.applications.launch(app.id, app.entryKey, app.argument)
|
|
64
56
|
setStartOpen(false)
|
|
65
57
|
}
|
|
66
58
|
|
|
@@ -74,7 +66,7 @@ export function DesktopShell({ desktop, apps = builtinApps, watermark = 'Open We
|
|
|
74
66
|
<div className="owd-shell__desktop r95-owd-desktop">
|
|
75
67
|
{watermark ? <div className="owd-shell__watermark">{watermark}</div> : null}
|
|
76
68
|
{activeWindows.map((win) => (
|
|
77
|
-
<DesktopWindow key={win.id} window={win} app={appById.get(win.
|
|
69
|
+
<DesktopWindow key={win.id} window={win} app={appById.get(win.applicationId ?? '')} />
|
|
78
70
|
))}
|
|
79
71
|
</div>
|
|
80
72
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Desktop } from '@owdproject/core'
|
|
2
|
+
import { registerReactApplication } from '@owdproject/react'
|
|
3
|
+
import type { DesktopApp } from './types'
|
|
4
|
+
|
|
5
|
+
/** Register shorthand apps once. Existing registrations remain owned by the consumer. */
|
|
6
|
+
export function registerDesktopApps(desktop: Desktop, apps: readonly DesktopApp[]): void {
|
|
7
|
+
for (const app of apps) {
|
|
8
|
+
if (desktop.applications.get(app.id) || (!app.component && !app.config)) continue
|
|
9
|
+
const { entries = {}, ...config } = app.config ?? {}
|
|
10
|
+
registerReactApplication(desktop, {
|
|
11
|
+
...config,
|
|
12
|
+
id: app.id,
|
|
13
|
+
title: app.title,
|
|
14
|
+
entries,
|
|
15
|
+
windows: config.windows ?? (app.component ? {
|
|
16
|
+
main: {
|
|
17
|
+
title: app.title,
|
|
18
|
+
size: app.defaultSize ?? { width: 480, height: 340 },
|
|
19
|
+
position: { x: 48, y: 40 },
|
|
20
|
+
component: app.component,
|
|
21
|
+
},
|
|
22
|
+
} : undefined),
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useReducer, useRef, type CSSProperties } from 'react'
|
|
2
2
|
import type { ResizeDirection, WindowBounds, WindowInstance, WindowState } from '@owdproject/core'
|
|
3
|
-
import { useDesktopDrag, useDesktopResize, useWindowInstance } from '@owdproject/react'
|
|
3
|
+
import { closeReactApplicationWindow, ReactApplicationWindow, useDesktopDrag, useDesktopResize, useWindowInstance } from '@owdproject/react'
|
|
4
4
|
import { OwdWindow } from '@owdproject/react-kit-react95'
|
|
5
5
|
import { useDesktopRuntime } from '../desktop-context'
|
|
6
6
|
import type { DesktopApp } from '../types'
|
|
@@ -88,11 +88,10 @@ export function DesktopWindow({ window: instance, app }: DesktopWindowProps) {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
const focus = () => desktop.windows.focusWindow(instance.id)
|
|
91
|
-
const AppComponent = app?.component
|
|
92
91
|
|
|
93
92
|
return (
|
|
94
93
|
<OwdWindow
|
|
95
|
-
title={
|
|
94
|
+
title={instance.config.title ?? app?.title ?? instance.id}
|
|
96
95
|
icon={app?.icon}
|
|
97
96
|
active={state.focused}
|
|
98
97
|
maximized={maximized}
|
|
@@ -104,6 +103,8 @@ export function DesktopWindow({ window: instance, app }: DesktopWindowProps) {
|
|
|
104
103
|
onPointerDownCapture={focus}
|
|
105
104
|
onTitlePointerDown={(event) => {
|
|
106
105
|
if (event.button !== 0) return
|
|
106
|
+
// Capturing pointers from title-bar controls prevents their click action.
|
|
107
|
+
if (event.target instanceof Element && event.target.closest('button, [role="button"]')) return
|
|
107
108
|
focus()
|
|
108
109
|
startDrag(event.nativeEvent)
|
|
109
110
|
}}
|
|
@@ -116,15 +117,15 @@ export function DesktopWindow({ window: instance, app }: DesktopWindowProps) {
|
|
|
116
117
|
focus()
|
|
117
118
|
startResize(event.nativeEvent, direction as ResizeDirection)
|
|
118
119
|
}}
|
|
119
|
-
onClose={() => desktop
|
|
120
|
+
onClose={() => { void closeReactApplicationWindow(desktop, instance) }}
|
|
120
121
|
onMinimize={() => instance.minimize()}
|
|
121
122
|
onToggleMaximize={() => {
|
|
122
123
|
if (state.maximizable === false) return
|
|
123
124
|
instance.setLayout(maximized ? 'normal' : 'maximize', getWorkarea())
|
|
124
125
|
}}
|
|
125
126
|
>
|
|
126
|
-
{
|
|
127
|
-
<
|
|
127
|
+
{instance.config.component ? (
|
|
128
|
+
<ReactApplicationWindow window={instance} desktop={desktop} />
|
|
128
129
|
) : (
|
|
129
130
|
<div className="owd-window__empty">No component registered for “{instance.model}”.</div>
|
|
130
131
|
)}
|
|
@@ -46,8 +46,8 @@ export function SystemBar({ apps, onStartClick, startActive }: SystemBarProps) {
|
|
|
46
46
|
|
|
47
47
|
<Frame className="owd-taskbar__tasks">
|
|
48
48
|
{windows.map((win) => {
|
|
49
|
-
const app = appById.get(win.
|
|
50
|
-
const title =
|
|
49
|
+
const app = appById.get(win.applicationId ?? '')
|
|
50
|
+
const title = win.config.title ?? app?.title ?? win.id
|
|
51
51
|
const isActive = focused?.id === win.id
|
|
52
52
|
return (
|
|
53
53
|
<Button
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,6 @@ export { DesktopWindow, type DesktopWindowProps } from './components/DesktopWind
|
|
|
11
11
|
export { SystemBar, type SystemBarProps } from './components/SystemBar'
|
|
12
12
|
export { StartMenuPanel, type StartMenuPanelProps } from './components/StartMenuPanel'
|
|
13
13
|
export { useDesktopRuntime, DesktopRuntimeContext, type DesktopRuntime } from './desktop-context'
|
|
14
|
-
export { builtinApps, AboutApp, NotepadApp } from './apps'
|
|
15
14
|
export type { DesktopApp, DesktopAppRenderProps } from './types'
|
|
16
15
|
|
|
17
16
|
// Re-export the kit primitives so consumers get one entry point.
|
package/src/theme.css
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
* Visual primitives come from @react95/core (via @owdproject/react-kit-react95).
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
/* The theme owns its kit: loading the theme styles also loads the kit's OWD
|
|
7
|
+
window glue, so consumers only import this single stylesheet. */
|
|
8
|
+
@import '@owdproject/react-kit-react95/styles.css';
|
|
9
|
+
|
|
6
10
|
.owd-shell {
|
|
7
11
|
position: fixed;
|
|
8
12
|
inset: 0;
|
package/src/types.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import type { ComponentType, ReactElement } from 'react'
|
|
2
|
-
import type {
|
|
2
|
+
import type { ReactApplicationConfig, ReactApplicationWindowProps } from '@owdproject/react'
|
|
3
3
|
|
|
4
4
|
/** Props passed to every application window component. */
|
|
5
|
-
export
|
|
6
|
-
window: WindowInstance
|
|
7
|
-
desktop: Desktop
|
|
8
|
-
}
|
|
5
|
+
export type DesktopAppRenderProps = ReactApplicationWindowProps
|
|
9
6
|
|
|
10
7
|
/** A React application that can be launched from the start menu / task bar. */
|
|
11
8
|
export interface DesktopApp {
|
|
@@ -14,5 +11,12 @@ export interface DesktopApp {
|
|
|
14
11
|
/** React95 icon used in the start menu, window title bar and task bar. */
|
|
15
12
|
icon?: ReactElement
|
|
16
13
|
defaultSize?: { width: number; height: number }
|
|
17
|
-
component
|
|
14
|
+
/** Optional shorthand for a single `main` window. Registered apps need no component here. */
|
|
15
|
+
component?: ComponentType<DesktopAppRenderProps>
|
|
16
|
+
/** Engine options, including entries, models, singleton and lifecycle callbacks. */
|
|
17
|
+
config?: Omit<ReactApplicationConfig, 'id' | 'title' | 'entries'> & {
|
|
18
|
+
entries?: ReactApplicationConfig['entries']
|
|
19
|
+
}
|
|
20
|
+
entryKey?: string
|
|
21
|
+
argument?: string
|
|
18
22
|
}
|
package/playground/index.html
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>Open Web Desktop — React win95 theme</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div id="root"></div>
|
|
10
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
-
</body>
|
|
12
|
-
</html>
|
package/playground/src/App.tsx
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef } from 'react'
|
|
2
|
-
import { createDesktop, type Desktop } from '@owdproject/core'
|
|
3
|
-
import { DesktopShell } from '../../src'
|
|
4
|
-
|
|
5
|
-
export function App() {
|
|
6
|
-
const desktopRef = useRef<Desktop | null>(null)
|
|
7
|
-
if (!desktopRef.current) {
|
|
8
|
-
desktopRef.current = createDesktop({
|
|
9
|
-
workspaces: ['workspace-1'],
|
|
10
|
-
workarea: { x: 0, y: 0, width: 0, height: 0 },
|
|
11
|
-
})
|
|
12
|
-
}
|
|
13
|
-
const desktop = desktopRef.current
|
|
14
|
-
|
|
15
|
-
// Open a welcome window on first mount.
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
desktop.windows.createWindow({
|
|
18
|
-
id: 'about-welcome',
|
|
19
|
-
model: 'about',
|
|
20
|
-
applicationId: 'about',
|
|
21
|
-
config: {
|
|
22
|
-
title: 'About Open Web Desktop',
|
|
23
|
-
size: { width: 380, height: 240 },
|
|
24
|
-
position: { x: 72, y: 56 },
|
|
25
|
-
},
|
|
26
|
-
})
|
|
27
|
-
}, [desktop])
|
|
28
|
-
|
|
29
|
-
return <DesktopShell desktop={desktop} watermark="React win95" />
|
|
30
|
-
}
|
package/playground/src/main.tsx
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { StrictMode } from 'react'
|
|
2
|
-
import { createRoot } from 'react-dom/client'
|
|
3
|
-
import '@owdproject/react-kit-react95/styles.css'
|
|
4
|
-
import '../../src/theme.css'
|
|
5
|
-
import { App } from './App'
|
|
6
|
-
|
|
7
|
-
const container = document.getElementById('root')
|
|
8
|
-
if (!container) throw new Error('#root not found')
|
|
9
|
-
|
|
10
|
-
createRoot(container).render(
|
|
11
|
-
<StrictMode>
|
|
12
|
-
<App />
|
|
13
|
-
</StrictMode>,
|
|
14
|
-
)
|
package/playground/tsconfig.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"moduleResolution": "bundler",
|
|
7
|
-
"jsx": "react-jsx",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"skipLibCheck": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"types": ["vite/client"],
|
|
12
|
-
"noEmit": true
|
|
13
|
-
},
|
|
14
|
-
"include": ["src/**/*", "vite.config.ts"]
|
|
15
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath, URL } from 'node:url'
|
|
2
|
-
import { defineConfig } from 'vite'
|
|
3
|
-
import react from '@vitejs/plugin-react'
|
|
4
|
-
|
|
5
|
-
const here = fileURLToPath(new URL('.', import.meta.url))
|
|
6
|
-
// pnpm workspace root (client/)
|
|
7
|
-
const workspaceRoot = fileURLToPath(new URL('../../..', import.meta.url))
|
|
8
|
-
|
|
9
|
-
export default defineConfig({
|
|
10
|
-
root: here,
|
|
11
|
-
plugins: [react()],
|
|
12
|
-
build: {
|
|
13
|
-
outDir: '../dist',
|
|
14
|
-
emptyOutDir: true,
|
|
15
|
-
rollupOptions: {
|
|
16
|
-
treeshake: {
|
|
17
|
-
// @react95/* ships no `sideEffects: false`, so Rollup otherwise keeps
|
|
18
|
-
// every icon/component module (and its CSS). Prune pure-JS modules but
|
|
19
|
-
// keep `.css`/`.css.mjs` so component styles still load.
|
|
20
|
-
moduleSideEffects(id) {
|
|
21
|
-
if (id.includes('@react95/')) return id.includes('.css')
|
|
22
|
-
return true
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
server: {
|
|
28
|
-
port: 5173,
|
|
29
|
-
fs: {
|
|
30
|
-
// Allow serving sources of the linked workspace packages.
|
|
31
|
-
allow: [workspaceRoot],
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
optimizeDeps: {
|
|
35
|
-
// Workspace packages are consumed as raw TypeScript sources.
|
|
36
|
-
exclude: [
|
|
37
|
-
'@owdproject/core',
|
|
38
|
-
'@owdproject/react',
|
|
39
|
-
'@owdproject/react-kit-react95',
|
|
40
|
-
'@owdproject/react-theme-win95',
|
|
41
|
-
],
|
|
42
|
-
},
|
|
43
|
-
})
|
package/src/apps/About.tsx
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Computer } from '@react95/icons'
|
|
2
|
-
import { Button } from '@owdproject/react-kit-react95'
|
|
3
|
-
import type { DesktopAppRenderProps } from '../types'
|
|
4
|
-
|
|
5
|
-
export function AboutApp({ window, desktop }: DesktopAppRenderProps) {
|
|
6
|
-
return (
|
|
7
|
-
<div className="owd-about">
|
|
8
|
-
<div className="owd-about__banner">
|
|
9
|
-
<Computer variant="32x32_4" />
|
|
10
|
-
<div>
|
|
11
|
-
<strong>Open Web Desktop</strong>
|
|
12
|
-
<div className="owd-about__small">React theme — win95 (React95)</div>
|
|
13
|
-
</div>
|
|
14
|
-
</div>
|
|
15
|
-
<hr className="owd-about__divider" />
|
|
16
|
-
<p className="owd-about__text">
|
|
17
|
-
Running on <code>@owdproject/core</code> + <code>@owdproject/react</code> +{' '}
|
|
18
|
-
<code>@react95/core</code>
|
|
19
|
-
<br />
|
|
20
|
-
Window <code>{window.id}</code> · model <code>{window.model}</code>
|
|
21
|
-
</p>
|
|
22
|
-
<div className="owd-about__actions">
|
|
23
|
-
<Button onClick={() => desktop.windows.closeWindow(window.id)}>OK</Button>
|
|
24
|
-
</div>
|
|
25
|
-
</div>
|
|
26
|
-
)
|
|
27
|
-
}
|
package/src/apps/Notepad.tsx
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react'
|
|
2
|
-
import type { DesktopAppRenderProps } from '../types'
|
|
3
|
-
|
|
4
|
-
export function NotepadApp(_props: DesktopAppRenderProps) {
|
|
5
|
-
const [text, setText] = useState(
|
|
6
|
-
'Hello from a React window.\r\n\r\nDrag the title bar to move me, or grab an edge to resize.',
|
|
7
|
-
)
|
|
8
|
-
return (
|
|
9
|
-
<div className="owd-notepad">
|
|
10
|
-
<div className="owd-notepad__menu">
|
|
11
|
-
<span>File</span>
|
|
12
|
-
<span>Edit</span>
|
|
13
|
-
<span>Search</span>
|
|
14
|
-
<span>Help</span>
|
|
15
|
-
</div>
|
|
16
|
-
<textarea
|
|
17
|
-
className="owd-notepad__area"
|
|
18
|
-
value={text}
|
|
19
|
-
spellCheck={false}
|
|
20
|
-
onChange={(event) => setText(event.target.value)}
|
|
21
|
-
/>
|
|
22
|
-
</div>
|
|
23
|
-
)
|
|
24
|
-
}
|
package/src/apps/index.tsx
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { DesktopApp } from '../types'
|
|
2
|
-
import { InfoBubble, Notepad } from '@react95/icons'
|
|
3
|
-
import { AboutApp } from './About'
|
|
4
|
-
import { NotepadApp } from './Notepad'
|
|
5
|
-
|
|
6
|
-
/** Built-in apps shipped with the win95 React theme. */
|
|
7
|
-
export const builtinApps: DesktopApp[] = [
|
|
8
|
-
{
|
|
9
|
-
id: 'about',
|
|
10
|
-
title: 'About Open Web Desktop',
|
|
11
|
-
icon: <InfoBubble variant="32x32_4" />,
|
|
12
|
-
defaultSize: { width: 380, height: 240 },
|
|
13
|
-
component: AboutApp,
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
id: 'notepad',
|
|
17
|
-
title: 'Untitled - Notepad',
|
|
18
|
-
icon: <Notepad variant="16x16_4" />,
|
|
19
|
-
defaultSize: { width: 480, height: 340 },
|
|
20
|
-
component: NotepadApp,
|
|
21
|
-
},
|
|
22
|
-
]
|
|
23
|
-
|
|
24
|
-
export { AboutApp, NotepadApp }
|