plusui-native 0.2.4 → 0.2.5

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.
@@ -142,20 +142,18 @@ just help # Show all commands
142
142
 
143
143
  ## Calling C++ from TypeScript
144
144
 
145
- Use the `plusui-native-core` SDK to interact with the native backend:
145
+ Use the local `frontend/src/plusui.ts` bridge to interact with the native backend:
146
146
 
147
147
  ```tsx
148
- import { createPlusUI } from 'plusui-native-core';
149
-
150
- const plusui = createPlusUI();
148
+ import { win } from './plusui';
151
149
 
152
150
  // Window controls
153
- await plusui.window.minimize();
154
- await plusui.window.maximize();
155
- await plusui.window.close();
151
+ await win.minimize();
152
+ await win.maximize();
153
+ await win.close();
156
154
 
157
155
  // Get window info
158
- const size = await plusui.window.getSize();
156
+ const size = await win.getSize();
159
157
  console.log(`Window size: ${size.width}x${size.height}`);
160
158
  ```
161
159
 
@@ -164,5 +162,5 @@ console.log(`Window size: ${size.width}x${size.height}`);
164
162
  - [PlusUI Documentation](https://github.com/yourorg/plusui)
165
163
  - [React Documentation](https://react.dev/)
166
164
  - [Vite Documentation](https://vitejs.dev/)
167
-
168
-
165
+
166
+
@@ -1,217 +1,173 @@
1
- import { mkdir, readdir, readFile, writeFile, copyFile, stat } from 'fs/promises';
2
- import { join, dirname, resolve } from 'path';
3
- import { existsSync } from 'fs';
4
- import { fileURLToPath } from 'url';
5
- import { EnvironmentDoctor } from '../src/doctor/index.js';
6
- import chalk from 'chalk';
7
- import { execSync, spawn } from 'child_process';
8
-
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = dirname(__filename);
11
-
12
- export class TemplateManager {
13
- constructor() {
14
- this.templatesDir = __dirname;
15
- }
16
-
17
- async create(projectName, options = {}) {
18
- const template = options.template || 'react';
19
- const templatePath = join(this.templatesDir, template);
20
-
21
- console.log(chalk.bold(`\nCreating PlusUI project: ${projectName}\n`));
22
-
23
- // 1. Check environment first
24
- console.log(chalk.blue('Checking environment...'));
25
- const doctor = new EnvironmentDoctor();
26
- const quickCheck = await doctor.quickCheck();
27
-
28
- if (!quickCheck.ready) {
29
- console.log(chalk.red('\n✗ Environment not ready!\n'));
30
- console.log(chalk.yellow('Run: plusui doctor --fix\n'));
31
- throw new Error('Environment check failed');
32
- }
33
-
34
- console.log(chalk.green('✓ Environment ready\n'));
35
-
36
- // 2. Create project directory
37
- console.log(chalk.blue('Creating project structure...'));
38
- const projectPath = resolve(projectName);
39
-
40
- if (existsSync(projectPath)) {
41
- throw new Error(`Directory ${projectName} already exists`);
42
- }
43
-
44
- await mkdir(projectPath, { recursive: true });
45
-
46
- // 3. Prepare template variables
47
- const variables = {
48
- PROJECT_NAME: projectName,
49
- PROJECT_NAME_LOWER: projectName.toLowerCase(),
50
- PROJECT_VERSION: '0.1.0',
51
- PLUSUI_CORE_PATH: this.resolvePlusUIPath()
52
- };
53
-
54
- // 4. Copy template files
55
- await this.copyTemplate(templatePath, projectPath, variables);
56
-
57
- // 5. Copy base files
58
- const basePath = join(this.templatesDir, 'base');
59
- await this.copyTemplate(basePath, projectPath, variables, true);
60
-
61
- console.log(chalk.green('✓ Project structure created\n'));
62
-
63
- // 6. Install npm dependencies
64
- console.log(chalk.blue('Installing dependencies...'));
65
- await this.runNpmInstall(projectPath);
66
- console.log(chalk.green('✓ Dependencies installed\n'));
67
-
68
- // 7. Print success message
69
- this.printSuccessMessage(projectName);
70
-
71
- return { success: true, path: projectPath };
72
- }
73
-
74
- async copyTemplate(templatePath, destPath, variables, skipSubdirs = false) {
75
- if (!existsSync(templatePath)) {
76
- return;
77
- }
78
-
79
- const entries = await readdir(templatePath, { withFileTypes: true });
80
-
81
- for (const entry of entries) {
82
- const srcPath = join(templatePath, entry.name);
83
- const destName = entry.name.replace('.template', '');
84
- const destFilePath = join(destPath, destName);
85
-
86
- if (entry.isDirectory()) {
87
- if (!skipSubdirs) {
88
- await mkdir(destFilePath, { recursive: true });
89
- await this.copyTemplate(srcPath, destFilePath, variables, false);
90
- }
91
- } else {
92
- // Ensure parent directory exists
93
- await mkdir(dirname(destFilePath), { recursive: true });
94
-
95
- // Read file content
96
- let content = await readFile(srcPath, 'utf8');
97
-
98
- // Replace variables
99
- content = this.replaceVariables(content, variables);
100
-
101
- // Write to destination
102
- await writeFile(destFilePath, content, 'utf8');
103
- }
104
- }
105
- }
106
-
107
- replaceVariables(content, variables) {
108
- let result = content;
109
-
110
- for (const [key, value] of Object.entries(variables)) {
111
- const placeholder = `{{${key}}}`;
112
- result = result.split(placeholder).join(value);
113
- }
114
-
115
- return result;
116
- }
117
-
118
- resolvePlusUIPath() {
119
- // Strategy:
120
- // 1. Check for global plusui-native-core installation
121
- // 2. Check for local development version
122
- // 3. Fall back to npm registry
123
-
124
- try {
125
- // Check if running in development (from PlusUI repo)
126
- const localCorePath = resolve(__dirname, '../../plusui-sdk/packages/core');
127
- if (existsSync(localCorePath)) {
128
- // Convert Windows backslashes to forward slashes for JSON compatibility
129
- return `file:${localCorePath.replace(/\\/g, '/')}`;
130
- }
131
-
132
- // Check global installation
133
- const globalPath = this.resolveGlobalPackage('plusui-native-core');
134
- if (globalPath) {
135
- return globalPath;
136
- }
137
- } catch (e) {
138
- // Ignore errors, fall back to npm
139
- }
140
-
141
- // Fall back to npm registry (when published)
142
- return '^0.1.0';
143
- }
144
-
145
- resolveGlobalPackage(packageName) {
146
- try {
147
- const result = execSync(`npm root -g`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore'] });
148
- const globalRoot = result.trim();
149
- const packagePath = join(globalRoot, packageName);
150
-
151
- if (existsSync(packagePath)) {
152
- return packagePath;
153
- }
154
- } catch (e) {
155
- // npm not available or package not found
156
- }
157
-
158
- return null;
159
- }
160
-
161
- async runNpmInstall(projectPath) {
162
- return new Promise((resolve, reject) => {
163
- const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
164
-
165
- // Install root dependencies
166
- const rootProc = spawn(npm, ['install'], {
167
- cwd: projectPath,
168
- stdio: 'inherit',
169
- shell: true
170
- });
171
-
172
- rootProc.on('close', (code) => {
173
- if (code !== 0) {
174
- reject(new Error(`npm install failed with code ${code}`));
175
- return;
176
- }
177
-
178
- // Install frontend dependencies
179
- const frontendPath = join(projectPath, 'frontend');
180
- if (existsSync(join(frontendPath, 'package.json'))) {
181
- const frontendProc = spawn(npm, ['install'], {
182
- cwd: frontendPath,
183
- stdio: 'inherit',
184
- shell: true
185
- });
186
-
187
- frontendProc.on('close', (feCode) => {
188
- if (feCode === 0) {
189
- resolve();
190
- } else {
191
- reject(new Error(`frontend npm install failed with code ${feCode}`));
192
- }
193
- });
194
-
195
- frontendProc.on('error', reject);
196
- } else {
197
- resolve();
198
- }
199
- });
200
-
201
- rootProc.on('error', reject);
202
- });
203
- }
204
-
205
- printSuccessMessage(projectName) {
206
- console.log(chalk.green.bold('\n✓ Project created successfully!\n'));
207
- console.log(chalk.bold('Next steps:\n'));
208
- console.log(chalk.cyan(` cd ${projectName}`));
209
- console.log(chalk.cyan(` npm run dev`));
210
- console.log();
211
- console.log(chalk.gray('This will start the development server with hot reload.'));
212
- console.log(chalk.gray('Edit frontend/src/App.tsx and main.cpp to get started!'));
213
- console.log();
214
- }
215
- }
216
-
217
-
1
+ import { mkdir, readdir, readFile, writeFile, copyFile, stat } from 'fs/promises';
2
+ import { join, dirname, resolve } from 'path';
3
+ import { existsSync } from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import { EnvironmentDoctor } from '../src/doctor/index.js';
6
+ import chalk from 'chalk';
7
+ import { spawn } from 'child_process';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = dirname(__filename);
11
+
12
+ export class TemplateManager {
13
+ constructor() {
14
+ this.templatesDir = __dirname;
15
+ }
16
+
17
+ async create(projectName, options = {}) {
18
+ const template = options.template || 'react';
19
+ const templatePath = join(this.templatesDir, template);
20
+
21
+ console.log(chalk.bold(`\nCreating PlusUI project: ${projectName}\n`));
22
+
23
+ // 1. Check environment first
24
+ console.log(chalk.blue('Checking environment...'));
25
+ const doctor = new EnvironmentDoctor();
26
+ const quickCheck = await doctor.quickCheck();
27
+
28
+ if (!quickCheck.ready) {
29
+ console.log(chalk.red('\n✗ Environment not ready!\n'));
30
+ console.log(chalk.yellow('Run: plusui doctor --fix\n'));
31
+ throw new Error('Environment check failed');
32
+ }
33
+
34
+ console.log(chalk.green('✓ Environment ready\n'));
35
+
36
+ // 2. Create project directory
37
+ console.log(chalk.blue('Creating project structure...'));
38
+ const projectPath = resolve(projectName);
39
+
40
+ if (existsSync(projectPath)) {
41
+ throw new Error(`Directory ${projectName} already exists`);
42
+ }
43
+
44
+ await mkdir(projectPath, { recursive: true });
45
+
46
+ // 3. Prepare template variables
47
+ const variables = {
48
+ PROJECT_NAME: projectName,
49
+ PROJECT_NAME_LOWER: projectName.toLowerCase(),
50
+ PROJECT_VERSION: '0.1.0'
51
+ };
52
+
53
+ // 4. Copy template files
54
+ await this.copyTemplate(templatePath, projectPath, variables);
55
+
56
+ // 5. Copy base files
57
+ const basePath = join(this.templatesDir, 'base');
58
+ await this.copyTemplate(basePath, projectPath, variables, true);
59
+
60
+ console.log(chalk.green('✓ Project structure created\n'));
61
+
62
+ // 6. Install npm dependencies
63
+ console.log(chalk.blue('Installing dependencies...'));
64
+ await this.runNpmInstall(projectPath);
65
+ console.log(chalk.green('✓ Dependencies installed\n'));
66
+
67
+ // 7. Print success message
68
+ this.printSuccessMessage(projectName);
69
+
70
+ return { success: true, path: projectPath };
71
+ }
72
+
73
+ async copyTemplate(templatePath, destPath, variables, skipSubdirs = false) {
74
+ if (!existsSync(templatePath)) {
75
+ return;
76
+ }
77
+
78
+ const entries = await readdir(templatePath, { withFileTypes: true });
79
+
80
+ for (const entry of entries) {
81
+ const srcPath = join(templatePath, entry.name);
82
+ const destName = entry.name.replace('.template', '');
83
+ const destFilePath = join(destPath, destName);
84
+
85
+ if (entry.isDirectory()) {
86
+ if (!skipSubdirs) {
87
+ await mkdir(destFilePath, { recursive: true });
88
+ await this.copyTemplate(srcPath, destFilePath, variables, false);
89
+ }
90
+ } else {
91
+ // Ensure parent directory exists
92
+ await mkdir(dirname(destFilePath), { recursive: true });
93
+
94
+ // Read file content
95
+ let content = await readFile(srcPath, 'utf8');
96
+
97
+ // Replace variables
98
+ content = this.replaceVariables(content, variables);
99
+
100
+ // Write to destination
101
+ await writeFile(destFilePath, content, 'utf8');
102
+ }
103
+ }
104
+ }
105
+
106
+ replaceVariables(content, variables) {
107
+ let result = content;
108
+
109
+ for (const [key, value] of Object.entries(variables)) {
110
+ const placeholder = `{{${key}}}`;
111
+ result = result.split(placeholder).join(value);
112
+ }
113
+
114
+ return result;
115
+ }
116
+
117
+ async runNpmInstall(projectPath) {
118
+ return new Promise((resolve, reject) => {
119
+ const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
120
+
121
+ // Install root dependencies
122
+ const rootProc = spawn(npm, ['install'], {
123
+ cwd: projectPath,
124
+ stdio: 'inherit',
125
+ shell: true
126
+ });
127
+
128
+ rootProc.on('close', (code) => {
129
+ if (code !== 0) {
130
+ reject(new Error(`npm install failed with code ${code}`));
131
+ return;
132
+ }
133
+
134
+ // Install frontend dependencies
135
+ const frontendPath = join(projectPath, 'frontend');
136
+ if (existsSync(join(frontendPath, 'package.json'))) {
137
+ const frontendProc = spawn(npm, ['install'], {
138
+ cwd: frontendPath,
139
+ stdio: 'inherit',
140
+ shell: true
141
+ });
142
+
143
+ frontendProc.on('close', (feCode) => {
144
+ if (feCode === 0) {
145
+ resolve();
146
+ } else {
147
+ reject(new Error(`frontend npm install failed with code ${feCode}`));
148
+ }
149
+ });
150
+
151
+ frontendProc.on('error', reject);
152
+ } else {
153
+ resolve();
154
+ }
155
+ });
156
+
157
+ rootProc.on('error', reject);
158
+ });
159
+ }
160
+
161
+ printSuccessMessage(projectName) {
162
+ console.log(chalk.green.bold('\n✓ Project created successfully!\n'));
163
+ console.log(chalk.bold('Next steps:\n'));
164
+ console.log(chalk.cyan(` cd ${projectName}`));
165
+ console.log(chalk.cyan(` npm run dev`));
166
+ console.log();
167
+ console.log(chalk.gray('This will start the development server with hot reload.'));
168
+ console.log(chalk.gray('Edit frontend/src/App.tsx and main.cpp to get started!'));
169
+ console.log();
170
+ }
171
+ }
172
+
173
+
@@ -24,7 +24,15 @@ endif()
24
24
  # Strategy: Check multiple locations for the Core library
25
25
  set(PLUSUI_FOUND FALSE)
26
26
 
27
- # Location 1: Local development (sibling to project inside PlusUI repo)
27
+ # Location 1: Project-local Core directory
28
+ set(PLUSUI_CORE_LOCAL "${CMAKE_SOURCE_DIR}/Core")
29
+ if(EXISTS "${PLUSUI_CORE_LOCAL}/CMakeLists.txt")
30
+ message(STATUS "Found PlusUI Core at: ${PLUSUI_CORE_LOCAL}")
31
+ add_subdirectory("${PLUSUI_CORE_LOCAL}" "${CMAKE_BINARY_DIR}/plusui-core")
32
+ set(PLUSUI_FOUND TRUE)
33
+ endif()
34
+
35
+ # Location 2: Local development (sibling to project inside PlusUI repo)
28
36
  set(PLUSUI_CORE_DEV "${CMAKE_SOURCE_DIR}/../Core")
29
37
  if(EXISTS "${PLUSUI_CORE_DEV}/CMakeLists.txt")
30
38
  message(STATUS "Found PlusUI Core at: ${PLUSUI_CORE_DEV}")
@@ -32,17 +40,23 @@ if(EXISTS "${PLUSUI_CORE_DEV}/CMakeLists.txt")
32
40
  set(PLUSUI_FOUND TRUE)
33
41
  endif()
34
42
 
35
- # Location 2: Installed via npm (node_modules)
43
+ # Location 3: Installed via npm (separate core package)
36
44
  if(NOT PLUSUI_FOUND)
37
- set(PLUSUI_CORE_NPM "${CMAKE_SOURCE_DIR}/node_modules/plusui-native-core/Core")
45
+ set(PLUSUI_CORE_NPM "${CMAKE_SOURCE_DIR}/node_modules/plusui-native-core")
46
+ set(PLUSUI_CORE_NPM_LEGACY "${CMAKE_SOURCE_DIR}/node_modules/plusui-native-core/Core")
47
+
38
48
  if(EXISTS "${PLUSUI_CORE_NPM}/CMakeLists.txt")
39
49
  message(STATUS "Found PlusUI Core in node_modules: ${PLUSUI_CORE_NPM}")
40
50
  add_subdirectory("${PLUSUI_CORE_NPM}" "${CMAKE_BINARY_DIR}/plusui-core")
41
51
  set(PLUSUI_FOUND TRUE)
52
+ elseif(EXISTS "${PLUSUI_CORE_NPM_LEGACY}/CMakeLists.txt")
53
+ message(STATUS "Found PlusUI Core in node_modules (legacy): ${PLUSUI_CORE_NPM_LEGACY}")
54
+ add_subdirectory("${PLUSUI_CORE_NPM_LEGACY}" "${CMAKE_BINARY_DIR}/plusui-core")
55
+ set(PLUSUI_FOUND TRUE)
42
56
  endif()
43
57
  endif()
44
58
 
45
- # Location 3: Parent directory development structure
59
+ # Location 4: Parent directory development structure
46
60
  if(NOT PLUSUI_FOUND)
47
61
  set(PLUSUI_CORE_PARENT "${CMAKE_SOURCE_DIR}/../../Core")
48
62
  if(EXISTS "${PLUSUI_CORE_PARENT}/CMakeLists.txt")
@@ -56,11 +70,13 @@ if(NOT PLUSUI_FOUND)
56
70
  message(FATAL_ERROR "
57
71
  PlusUI Core not found!
58
72
 
59
- Make sure you have run 'npm install' or that this project is inside the PlusUI repository.
73
+ Install dependencies (npm install) or add a Core folder in this project root.
60
74
 
61
75
  Searched locations:
76
+ - ${PLUSUI_CORE_LOCAL}
62
77
  - ${PLUSUI_CORE_DEV}
63
- - ${PLUSUI_CORE_NPM}
78
+ - ${PLUSUI_CORE_NPM}
79
+ - ${PLUSUI_CORE_NPM_LEGACY}
64
80
  - ${PLUSUI_CORE_PARENT}
65
81
  ")
66
82
  endif()
@@ -147,5 +163,5 @@ endif()
147
163
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
148
164
  set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
149
165
  set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
150
-
151
-
166
+
167
+
@@ -10,8 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "react": "^18.2.0",
13
- "react-dom": "^18.2.0",
14
- "plusui-native-core": "{{PLUSUI_CORE_PATH}}"
13
+ "react-dom": "^18.2.0"
15
14
  },
16
15
  "devDependencies": {
17
16
  "@types/react": "^18.2.0",
@@ -20,5 +19,5 @@
20
19
  "typescript": "^5.3.0",
21
20
  "vite": "^5.0.0"
22
21
  }
23
- }
24
-
22
+ }
23
+
@@ -1,5 +1,5 @@
1
1
  import { useState, useEffect } from 'react';
2
- import { win, browser, router, app } from 'plusui-native-core';
2
+ import { win, browser, router, app } from './plusui';
3
3
 
4
4
  // Define routes for your app (optional - for SPA routing)
5
5
  const routes = {
@@ -130,5 +130,5 @@ function App() {
130
130
  );
131
131
  }
132
132
 
133
- export default App;
134
-
133
+ export default App;
134
+
@@ -0,0 +1,117 @@
1
+ type InvokeFn = (method: string, args?: unknown[]) => Promise<unknown>;
2
+ type PendingMap = Record<string, { resolve: (value: unknown) => void; reject: (reason?: unknown) => void }>;
3
+
4
+ type WindowSize = { width: number; height: number };
5
+ type WindowPosition = { x: number; y: number };
6
+ type RouteMap = Record<string, string>;
7
+
8
+ let _invoke: InvokeFn | null = null;
9
+ let _pending: PendingMap = {};
10
+ let _routes: RouteMap = {};
11
+
12
+ function initBridge() {
13
+ if (typeof window === 'undefined') return;
14
+
15
+ const w = window as any;
16
+ if (typeof w.__invoke__ === 'function') {
17
+ _invoke = w.__invoke__ as InvokeFn;
18
+ return;
19
+ }
20
+
21
+ _pending = {};
22
+ w.__pending__ = _pending;
23
+
24
+ w.__invoke__ = (method: string, args?: unknown[]): Promise<unknown> => {
25
+ return new Promise((resolve, reject) => {
26
+ const id = Math.random().toString(36).slice(2, 11);
27
+ const request = JSON.stringify({ id, method, params: args ?? [] });
28
+
29
+ _pending[id] = { resolve, reject };
30
+
31
+ if (typeof w.__native_invoke__ === 'function') {
32
+ w.__native_invoke__(request);
33
+ } else {
34
+ setTimeout(() => {
35
+ delete _pending[id];
36
+ resolve(null);
37
+ }, 0);
38
+ }
39
+
40
+ setTimeout(() => {
41
+ if (_pending[id]) {
42
+ delete _pending[id];
43
+ reject(new Error(`${method} timed out`));
44
+ }
45
+ }, 30000);
46
+ });
47
+ };
48
+
49
+ w.__response__ = (id: string, result: unknown) => {
50
+ const pending = _pending[id];
51
+ if (pending) {
52
+ pending.resolve(result);
53
+ delete _pending[id];
54
+ }
55
+ };
56
+
57
+ _invoke = w.__invoke__ as InvokeFn;
58
+ }
59
+
60
+ async function invoke(method: string, args?: unknown[]) {
61
+ if (!_invoke) {
62
+ initBridge();
63
+ if (!_invoke) {
64
+ throw new Error('PlusUI bridge not initialized');
65
+ }
66
+ }
67
+
68
+ return _invoke(method, args);
69
+ }
70
+
71
+ initBridge();
72
+
73
+ export const win = {
74
+ minimize: async () => invoke('window.minimize', []),
75
+ maximize: async () => invoke('window.maximize', []),
76
+ close: async () => invoke('window.close', []),
77
+ setPosition: async (x: number, y: number) => invoke('window.setPosition', [x, y]),
78
+ getSize: async (): Promise<WindowSize> => invoke('window.getSize', []) as Promise<WindowSize>,
79
+ getPosition: async (): Promise<WindowPosition> => invoke('window.getPosition', []) as Promise<WindowPosition>,
80
+ };
81
+
82
+ export const browser = {
83
+ getUrl: async (): Promise<string> => invoke('browser.getUrl', []) as Promise<string>,
84
+ goBack: async () => invoke('browser.goBack', []),
85
+ goForward: async () => invoke('browser.goForward', []),
86
+ reload: async () => invoke('browser.reload', []),
87
+ canGoBack: async (): Promise<boolean> => invoke('browser.canGoBack', []) as Promise<boolean>,
88
+ canGoForward: async (): Promise<boolean> => invoke('browser.canGoForward', []) as Promise<boolean>,
89
+ onNavigate: (handler: (url: string) => void) => {
90
+ if (typeof window === 'undefined') {
91
+ return () => {};
92
+ }
93
+
94
+ const eventHandler = (event: Event) => {
95
+ const custom = event as CustomEvent<{ url?: string }>;
96
+ const nextUrl = custom.detail?.url ?? '';
97
+ handler(nextUrl);
98
+ };
99
+
100
+ window.addEventListener('plusui:navigate', eventHandler);
101
+ return () => window.removeEventListener('plusui:navigate', eventHandler);
102
+ },
103
+ };
104
+
105
+ export const router = {
106
+ setRoutes: (routes: RouteMap) => {
107
+ _routes = routes;
108
+ },
109
+ push: async (path: string) => {
110
+ const target = _routes[path] ?? path;
111
+ return invoke('browser.navigate', [target]);
112
+ },
113
+ };
114
+
115
+ export const app = {
116
+ quit: async () => invoke('app.quit', []),
117
+ };
@@ -181,7 +181,7 @@ int main() {
181
181
  // ============================================================================
182
182
  // FRONTEND API REFERENCE
183
183
  // ============================================================================
184
- // import { win, browser, router, app, display, clipboard } from 'plusui-native-core';
184
+ // import { win, browser, router, app } from './frontend/src/plusui';
185
185
  //
186
186
  // WINDOW: win.minimize(), win.maximize(), win.close(), win.center(),
187
187
  // win.setSize(w, h), win.setPosition(x, y), win.setTitle(str),
@@ -197,5 +197,5 @@ int main() {
197
197
  //
198
198
  // DISPLAY: display.getAll(), display.getPrimary(), display.getCurrent()
199
199
  //
200
- // CLIPBOARD: clipboard.writeText(str), clipboard.readText(), clipboard.clear()
201
-
200
+ // CLIPBOARD: clipboard.writeText(str), clipboard.readText(), clipboard.clear()
201
+