plusui-native 0.2.4 → 0.2.7

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.
@@ -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
+
@@ -17,7 +17,8 @@
17
17
  "clean": "plusui clean"
18
18
  },
19
19
  "dependencies": {
20
- "plusui-native": "^0.2.0"
20
+ "plusui-native": "^0.2.0",
21
+ "plusui-native-core": "^0.1.4"
21
22
  }
22
23
  }
23
24
 
@@ -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()
@@ -101,13 +117,7 @@ if(WIN32)
101
117
  endif()
102
118
 
103
119
  target_link_libraries({{PROJECT_NAME}} PRIVATE ole32 shell32 shlwapi user32 version)
104
-
105
- # Set subsystem to Windows for release builds (no console window)
106
- if(NOT PLUSUI_DEV_MODE)
107
- set_target_properties({{PROJECT_NAME}} PROPERTIES
108
- WIN32_EXECUTABLE TRUE
109
- )
110
- endif()
120
+ # Keep default console subsystem so standard int main() works in all build modes.
111
121
  elseif(APPLE)
112
122
  # macOS: WebKit
113
123
  find_library(WEBKIT_LIBRARY WebKit REQUIRED)
@@ -147,5 +157,5 @@ endif()
147
157
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
148
158
  set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
149
159
  set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
150
-
151
-
160
+
161
+
@@ -9,13 +9,12 @@
9
9
  "preview": "vite preview"
10
10
  },
11
11
  "dependencies": {
12
- "solid-js": "^1.8.0",
13
- "plusui-native-core": "{{PLUSUI_CORE_PATH}}"
12
+ "solid-js": "^1.8.0"
14
13
  },
15
14
  "devDependencies": {
16
15
  "typescript": "^5.3.0",
17
16
  "vite": "^5.0.0",
18
17
  "vite-plugin-solid": "^2.10.0"
19
18
  }
20
- }
21
-
19
+ }
20
+
@@ -1,5 +1,5 @@
1
- import { createSignal, onMount } from 'solid-js';
2
- import { win, browser, router, app } from 'plusui-native-core';
1
+ import { createSignal, onMount, Show } from 'solid-js';
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 = {
@@ -48,7 +48,6 @@ function App() {
48
48
  const handleGoBack = async () => await browser.goBack();
49
49
  const handleGoForward = async () => await browser.goForward();
50
50
  const handleReload = async () => await browser.reload();
51
- const handleNavigate = async (url: string) => await browser.navigate(url);
52
51
 
53
52
  // Router navigation
54
53
  const handleGoHome = async () => await router.push('/');
@@ -129,5 +128,5 @@ function App() {
129
128
  );
130
129
  }
131
130
 
132
- export default App;
133
-
131
+ export default App;
132
+
@@ -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
+ };
@@ -172,7 +172,7 @@ int main() {
172
172
  // ============================================================================
173
173
  // FRONTEND API REFERENCE
174
174
  // ============================================================================
175
- // import { win, browser, router, app, display, clipboard } from 'plusui-native-core';
175
+ // import { win, browser, router, app } from './frontend/src/plusui';
176
176
  //
177
177
  // WINDOW: win.minimize(), win.maximize(), win.close(), win.center(),
178
178
  // win.setSize(w, h), win.setPosition(x, y), win.setTitle(str),
@@ -188,5 +188,5 @@ int main() {
188
188
  //
189
189
  // DISPLAY: display.getAll(), display.getPrimary(), display.getCurrent()
190
190
  //
191
- // CLIPBOARD: clipboard.writeText(str), clipboard.readText(), clipboard.clear()
192
-
191
+ // CLIPBOARD: clipboard.writeText(str), clipboard.readText(), clipboard.clear()
192
+
@@ -17,7 +17,8 @@
17
17
  "clean": "plusui clean"
18
18
  },
19
19
  "dependencies": {
20
- "plusui-native": "^0.2.0"
20
+ "plusui-native": "^0.2.0",
21
+ "plusui-native-core": "^0.1.4"
21
22
  }
22
23
  }
23
24