From 2c369d74b1da125ebb05bb1fdb8a2a35e57e3221 Mon Sep 17 00:00:00 2001 From: nonna Date: Wed, 11 Mar 2026 00:44:45 +0000 Subject: [PATCH] Initial Neutralino.js POC for kokoro-widget Lightweight alternative to the Electron build (~5MB vs ~300MB). Uses system webview (WebKit2GTK on Linux, WKWebView on macOS, WebView2 on Windows). Same UI and feature set as the Electron build: WebSocket audio client, volume control, host/port settings, tray icon, hide-to-tray on close, CLI args (--host, --port, --volume). Note: Linux requires libwebkit2gtk-4.0 or libwebkit2gtk-4.1. Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 13 + LICENSE | 21 ++ README.md | 6 + neutralino.config.json | 38 +++ package.json | 12 + www/app.js | 334 ++++++++++++++++++++++++++ www/icon.png | Bin 0 -> 16392 bytes www/index.html | 220 +++++++++++++++++ www/neutralino.d.ts | 531 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 1175 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 neutralino.config.json create mode 100644 package.json create mode 100644 www/app.js create mode 100644 www/icon.png create mode 100644 www/index.html create mode 100644 www/neutralino.d.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4a3dc9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Developer tools' files +.lite_workspace.lua + +# Neutralinojs binaries and builds +/bin +/dist + +# Neutralinojs client (minified) +neutralino.js + +# Neutralinojs related files +.storage +*.log diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..046f3db --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Neutralinojs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..995ac7e --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# neutralinojs-zero +An empty Neutralinojs app, extend as you wish + +``` +neu create myapp --template neutralinojs/neutralinojs-zero +``` diff --git a/neutralino.config.json b/neutralino.config.json new file mode 100644 index 0000000..523bb5c --- /dev/null +++ b/neutralino.config.json @@ -0,0 +1,38 @@ +{ + "applicationId": "com.pai.kokoro-widget", + "version": "1.0.0", + "defaultMode": "window", + "documentRoot": "/www/", + "url": "/", + "enableServer": true, + "enableNativeAPI": true, + "nativeAllowList": [ + "app.*", + "os.*", + "storage.*", + "window.*", + "events.*", + "debug.*" + ], + "modes": { + "window": { + "title": "PAI Voice", + "width": 360, + "height": 540, + "minWidth": 300, + "minHeight": 400, + "icon": "/www/icon.png", + "alwaysOnTop": false, + "borderless": false, + "maximize": false + } + }, + "cli": { + "binaryName": "kokoro-widget", + "resourcesPath": "/www/", + "extensionsPath": "/extensions/", + "clientLibrary": "/www/neutralino.js", + "binaryVersion": "6.5.0", + "clientVersion": "6.5.0" + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..2003550 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "kokoro-widget-neutralino", + "version": "1.0.0", + "description": "Standalone PAI Voice audio widget — lightweight Neutralino.js build", + "scripts": { + "start": "neu run", + "build": "neu build" + }, + "dependencies": { + "@neutralinojs/neu": "^11.7.0" + } +} diff --git a/www/app.js b/www/app.js new file mode 100644 index 0000000..ea5fc94 --- /dev/null +++ b/www/app.js @@ -0,0 +1,334 @@ +'use strict'; + +// --------------------------------------------------------------------------- +// Config defaults and storage key +// --------------------------------------------------------------------------- +const CONFIG_DEFAULTS = { host: 'localhost', port: 8888, volume: 80 }; +const STORAGE_KEY = 'kokoro-config'; + +// --------------------------------------------------------------------------- +// State +// --------------------------------------------------------------------------- +let config = { ...CONFIG_DEFAULTS }; +let ws = null; +let reconnectTimer = null; +const audioQueue = []; +const AUDIO_QUEUE_MAX = 25; +let isPlaying = false; +let consecutiveAudioErrors = 0; +const AUDIO_ERROR_LIMIT = 5; +let volumeSaveTimer = null; + +// DOM refs +let statusDot, statusText, speakingIndicator, lastMessage; +let volumeSlider, hostInput, portInput, saveBtn; + +// --------------------------------------------------------------------------- +// Config persistence via Neutralino storage +// --------------------------------------------------------------------------- +async function loadConfig() { + try { + const raw = await Neutralino.storage.getData(STORAGE_KEY); + return Object.assign({}, CONFIG_DEFAULTS, JSON.parse(raw)); + } catch { + return { ...CONFIG_DEFAULTS }; + } +} + +async function saveConfig(partial) { + config = Object.assign({}, config, partial); + try { + await Neutralino.storage.setData(STORAGE_KEY, JSON.stringify(config)); + } catch (e) { + console.error('Failed to save config:', e); + } + return config; +} + +// --------------------------------------------------------------------------- +// CLI argument parsing — Neutralino exposes args via NL_ARGS global +// --------------------------------------------------------------------------- +function parseCliArgs() { + const overrides = {}; + // NL_ARGS is a space-joined string of all args passed after -- + const args = (typeof NL_ARGS !== 'undefined' ? NL_ARGS : '').split(' ').filter(Boolean); + for (let i = 0; i < args.length; i++) { + switch (args[i]) { + case '--host': + if (args[i + 1]) overrides.host = args[++i]; + break; + case '--port': { + const p = parseInt(args[++i], 10); + if (!isNaN(p)) overrides.port = p; + break; + } + case '--volume': { + const v = parseInt(args[++i], 10); + if (!isNaN(v)) overrides.volume = Math.min(100, Math.max(0, v)); + break; + } + } + } + return overrides; +} + +// --------------------------------------------------------------------------- +// Tray setup +// --------------------------------------------------------------------------- +async function setupTray() { + try { + await Neutralino.os.setTray({ + icon: '/www/icon.png', + menuItems: [ + { id: 'tray_show', text: 'Show' }, + { id: 'tray_sep', text: '-' }, + { id: 'tray_quit', text: 'Quit' }, + ], + }); + } catch (e) { + console.warn('Tray setup failed (may not be supported on this platform):', e); + } +} + +// Handle tray menu clicks +Neutralino.events.on('trayMenuItemClicked', async (evt) => { + switch (evt.detail.id) { + case 'tray_show': + await Neutralino.window.show(); + await Neutralino.window.focus(); + break; + case 'tray_quit': + await Neutralino.app.exit(); + break; + } +}); + +// Intercept window close — hide to tray instead of quitting +Neutralino.events.on('windowClose', async () => { + await Neutralino.window.hide(); +}); + +// --------------------------------------------------------------------------- +// Status / speaking display +// --------------------------------------------------------------------------- +function setStatus(state, text) { + statusDot.className = 'status-dot ' + state; + statusText.textContent = text; +} + +function setSpeaking(active) { + speakingIndicator.classList.toggle('active', active); +} + +// --------------------------------------------------------------------------- +// WebSocket connection +// --------------------------------------------------------------------------- +function getWsUrl() { + return `ws://${config.host}:${config.port}/stream`; +} + +function connect() { + if (ws) { + ws.onopen = null; + ws.onmessage = null; + ws.onclose = null; + ws.onerror = null; + ws.close(); + ws = null; + } + + clearReconnectTimer(); + setStatus('connecting', 'Connecting...'); + + try { + ws = new WebSocket(getWsUrl()); + ws.binaryType = 'arraybuffer'; + + ws.onopen = () => { + setStatus('connected', 'Connected — waiting for audio'); + }; + + ws.onmessage = (event) => { + if (typeof event.data === 'string') { + try { + const msg = JSON.parse(event.data); + if (msg.type === 'notification' && msg.message) { + lastMessage.textContent = msg.message; + setStatus('connected', 'Connected'); + } + } catch { + // ignore malformed JSON + } + } else if (event.data instanceof ArrayBuffer) { + setStatus('connected', 'Connected'); + if (audioQueue.length < AUDIO_QUEUE_MAX) { + audioQueue.push(event.data); + if (!isPlaying) playNext(); + } else { + console.warn('Audio queue full — dropping frame'); + } + } + }; + + ws.onclose = () => { + const hadError = ws && ws._hadError; + ws = null; + setStatus(hadError ? 'error' : 'connecting', + hadError ? 'Disconnected — retrying...' : 'Reconnecting...'); + scheduleReconnect(); + }; + + ws.onerror = () => { + if (ws) ws._hadError = true; + setStatus('error', 'Connection error'); + }; + } catch (e) { + console.error('WebSocket connection failed:', e); + setStatus('error', 'Connection error'); + scheduleReconnect(); + } +} + +function scheduleReconnect() { + clearReconnectTimer(); + reconnectTimer = setTimeout(() => { + if (ws === null) connect(); + }, 3000); +} + +function clearReconnectTimer() { + if (reconnectTimer) { + clearTimeout(reconnectTimer); + reconnectTimer = null; + } +} + +// --------------------------------------------------------------------------- +// Audio playback queue +// --------------------------------------------------------------------------- +async function playNext() { + if (audioQueue.length === 0) { + isPlaying = false; + setSpeaking(false); + return; + } + + isPlaying = true; + setSpeaking(true); + const buffer = audioQueue.shift(); + let url = null; + + try { + const blob = new Blob([buffer], { type: 'audio/mpeg' }); + url = URL.createObjectURL(blob); + const audio = new Audio(url); + audio.volume = Math.min(1, Math.max(0, config.volume / 100)); + + audio.onended = () => { + URL.revokeObjectURL(url); + consecutiveAudioErrors = 0; + playNext(); + }; + + audio.onerror = (e) => { + console.error('Audio error:', e); + URL.revokeObjectURL(url); + consecutiveAudioErrors++; + if (consecutiveAudioErrors >= AUDIO_ERROR_LIMIT) { + isPlaying = false; + setSpeaking(false); + setStatus('error', 'Audio error'); + audioQueue.length = 0; + return; + } + playNext(); + }; + + await audio.play(); + consecutiveAudioErrors = 0; + } catch (e) { + console.error('Audio playback failed:', e); + if (url) URL.revokeObjectURL(url); + consecutiveAudioErrors++; + if (consecutiveAudioErrors >= AUDIO_ERROR_LIMIT) { + isPlaying = false; + setSpeaking(false); + setStatus('error', 'Audio error'); + audioQueue.length = 0; + return; + } + playNext(); + } +} + +// --------------------------------------------------------------------------- +// Initialization +// --------------------------------------------------------------------------- +async function init() { + // Wire DOM refs + statusDot = document.getElementById('statusDot'); + statusText = document.getElementById('statusText'); + speakingIndicator = document.getElementById('speakingIndicator'); + lastMessage = document.getElementById('lastMessage'); + volumeSlider = document.getElementById('volume'); + hostInput = document.getElementById('hostInput'); + portInput = document.getElementById('portInput'); + saveBtn = document.getElementById('saveBtn'); + + // Load persisted config then apply CLI overrides (CLI does not persist) + config = await loadConfig(); + const cliOverrides = parseCliArgs(); + if (cliOverrides.host !== undefined) config.host = cliOverrides.host; + if (cliOverrides.port !== undefined) config.port = cliOverrides.port; + if (cliOverrides.volume !== undefined) config.volume = cliOverrides.volume; + + // Populate UI + volumeSlider.value = config.volume; + hostInput.value = config.host; + portInput.value = config.port; + + // Volume: live update + debounce save + volumeSlider.addEventListener('input', () => { + config.volume = parseInt(volumeSlider.value, 10); + clearTimeout(volumeSaveTimer); + volumeSaveTimer = setTimeout(() => saveConfig({ volume: config.volume }), 500); + }); + + // Save button + saveBtn.addEventListener('click', async () => { + const newHost = hostInput.value.trim() || 'localhost'; + const newPort = parseInt(portInput.value, 10); + const validPort = (!isNaN(newPort) && newPort > 0 && newPort <= 65535) ? newPort : 8888; + + hostInput.value = newHost; + portInput.value = validPort; + + await saveConfig({ host: newHost, port: validPort }); + + saveBtn.textContent = 'Saved!'; + saveBtn.classList.add('saved'); + setTimeout(() => { + saveBtn.textContent = 'Save'; + saveBtn.classList.remove('saved'); + }, 1500); + + connect(); + }); + + // Draggable header + try { + await Neutralino.window.setDraggableRegion('dragRegion'); + } catch (e) { + console.warn('setDraggableRegion not supported:', e); + } + + // Set up tray + await setupTray(); + + // Connect to voice server + connect(); +} + +// Boot after Neutralino is ready +Neutralino.init(); +document.addEventListener('DOMContentLoaded', init); diff --git a/www/icon.png b/www/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d708bc3cc0f64c18aac9ff49d28476c02c9dca0c GIT binary patch literal 16392 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 + + + + + PAI Voice + + + +
+
+

PAI Voice

+

Voice notifications from your AI assistant

+
+ +
+ + Connecting... +
+ +
+
+
+
+
+
+
+
+
+ +
Waiting for notifications...
+ +
+ +
+ + +
+ +
+

Settings

+
+ + +
+
+ + +
+
+ +
+
+
+ + + + + diff --git a/www/neutralino.d.ts b/www/neutralino.d.ts new file mode 100644 index 0000000..09d69e6 --- /dev/null +++ b/www/neutralino.d.ts @@ -0,0 +1,531 @@ +export declare enum LoggerType { + WARNING = "WARNING", + ERROR = "ERROR", + INFO = "INFO" +} +export declare enum Icon { + WARNING = "WARNING", + ERROR = "ERROR", + INFO = "INFO", + QUESTION = "QUESTION" +} +export declare enum MessageBoxChoice { + OK = "OK", + OK_CANCEL = "OK_CANCEL", + YES_NO = "YES_NO", + YES_NO_CANCEL = "YES_NO_CANCEL", + RETRY_CANCEL = "RETRY_CANCEL", + ABORT_RETRY_IGNORE = "ABORT_RETRY_IGNORE" +} +export declare enum ClipboardFormat { + unknown = "unknown", + text = "text", + image = "image" +} +export declare enum Mode { + window = "window", + browser = "browser", + cloud = "cloud", + chrome = "chrome" +} +export declare enum OperatingSystem { + Linux = "Linux", + Windows = "Windows", + Darwin = "Darwin", + FreeBSD = "FreeBSD", + Unknown = "Unknown" +} +export declare enum Architecture { + x64 = "x64", + arm = "arm", + itanium = "itanium", + ia32 = "ia32", + unknown = "unknown" +} +export interface DirectoryEntry { + entry: string; + path: string; + type: string; +} +export interface FileReaderOptions { + pos: number; + size: number; +} +export interface DirectoryReaderOptions { + recursive: boolean; +} +export interface OpenedFile { + id: number; + eof: boolean; + pos: number; + lastRead: number; +} +export interface Stats { + size: number; + isFile: boolean; + isDirectory: boolean; + createdAt: number; + modifiedAt: number; +} +export interface Watcher { + id: number; + path: string; +} +export interface CopyOptions { + recursive: boolean; + overwrite: boolean; + skip: boolean; +} +export interface PathParts { + rootName: string; + rootDirectory: string; + rootPath: string; + relativePath: string; + parentPath: string; + filename: string; + stem: string; + extension: string; +} +interface Permissions$1 { + all: boolean; + ownerAll: boolean; + ownerRead: boolean; + ownerWrite: boolean; + ownerExec: boolean; + groupAll: boolean; + groupRead: boolean; + groupWrite: boolean; + groupExec: boolean; + othersAll: boolean; + othersRead: boolean; + othersWrite: boolean; + othersExec: boolean; +} +export type PermissionsMode = "ADD" | "REPLACE" | "REMOVE"; +declare function createDirectory(path: string): Promise; +declare function remove(path: string): Promise; +declare function writeFile(path: string, data: string): Promise; +declare function appendFile(path: string, data: string): Promise; +declare function writeBinaryFile(path: string, data: ArrayBuffer): Promise; +declare function appendBinaryFile(path: string, data: ArrayBuffer): Promise; +declare function readFile(path: string, options?: FileReaderOptions): Promise; +declare function readBinaryFile(path: string, options?: FileReaderOptions): Promise; +declare function openFile(path: string): Promise; +declare function createWatcher(path: string): Promise; +declare function removeWatcher(id: number): Promise; +declare function getWatchers(): Promise; +declare function updateOpenedFile(id: number, event: string, data?: any): Promise; +declare function getOpenedFileInfo(id: number): Promise; +declare function readDirectory(path: string, options?: DirectoryReaderOptions): Promise; +declare function copy(source: string, destination: string, options?: CopyOptions): Promise; +declare function move(source: string, destination: string): Promise; +declare function getStats(path: string): Promise; +declare function getAbsolutePath(path: string): Promise; +declare function getRelativePath(path: string, base?: string): Promise; +declare function getPathParts(path: string): Promise; +declare function getPermissions(path: string): Promise; +declare function setPermissions(path: string, permissions: Permissions$1, mode: PermissionsMode): Promise; +declare function getJoinedPath(...paths: string[]): Promise; +declare function getNormalizedPath(path: string): Promise; +declare function getUnnormalizedPath(path: string): Promise; +export interface ExecCommandOptions { + stdIn?: string; + background?: boolean; + cwd?: string; +} +export interface ExecCommandResult { + pid: number; + stdOut: string; + stdErr: string; + exitCode: number; +} +export interface SpawnedProcess { + id: number; + pid: number; +} +export interface SpawnedProcessOptions { + cwd?: string; + envs?: Record; +} +export interface Envs { + [key: string]: string; +} +export interface OpenDialogOptions { + multiSelections?: boolean; + filters?: Filter[]; + defaultPath?: string; +} +export interface FolderDialogOptions { + defaultPath?: string; +} +export interface SaveDialogOptions { + forceOverwrite?: boolean; + filters?: Filter[]; + defaultPath?: string; +} +export interface Filter { + name: string; + extensions: string[]; +} +export interface TrayOptions { + icon: string; + menuItems: TrayMenuItem[]; +} +export interface TrayMenuItem { + id?: string; + text: string; + isDisabled?: boolean; + isChecked?: boolean; +} +export type KnownPath = "config" | "data" | "cache" | "documents" | "pictures" | "music" | "video" | "downloads" | "savedGames1" | "savedGames2" | "temp"; +declare function execCommand(command: string, options?: ExecCommandOptions): Promise; +declare function spawnProcess(command: string, options?: SpawnedProcessOptions): Promise; +declare function updateSpawnedProcess(id: number, event: string, data?: any): Promise; +declare function getSpawnedProcesses(): Promise; +declare function getEnv(key: string): Promise; +declare function getEnvs(): Promise; +declare function showOpenDialog(title?: string, options?: OpenDialogOptions): Promise; +declare function showFolderDialog(title?: string, options?: FolderDialogOptions): Promise; +declare function showSaveDialog(title?: string, options?: SaveDialogOptions): Promise; +declare function showNotification(title: string, content: string, icon?: Icon): Promise; +declare function showMessageBox(title: string, content: string, choice?: MessageBoxChoice, icon?: Icon): Promise; +declare function setTray(options: TrayOptions): Promise; +declare function open$1(url: string): Promise; +declare function getPath(name: KnownPath): Promise; +export interface MemoryInfo { + physical: { + total: number; + available: number; + }; + virtual: { + total: number; + available: number; + }; +} +export interface KernelInfo { + variant: string; + version: string; +} +export interface OSInfo { + name: string; + description: string; + version: string; +} +export interface CPUInfo { + vendor: string; + model: string; + frequency: number; + architecture: string; + logicalThreads: number; + physicalCores: number; + physicalUnits: number; +} +export interface Display { + id: number; + resolution: Resolution; + dpi: number; + bpp: number; + refreshRate: number; +} +export interface Resolution { + width: number; + height: number; +} +export interface MousePosition { + x: number; + y: number; +} +declare function getMemoryInfo(): Promise; +declare function getArch(): Promise; +declare function getKernelInfo(): Promise; +declare function getOSInfo(): Promise; +declare function getCPUInfo(): Promise; +declare function getDisplays(): Promise; +declare function getMousePosition(): Promise; +declare function setData(key: string, data: string | null): Promise; +declare function getData(key: string): Promise; +declare function removeData(key: string): Promise; +declare function getKeys(): Promise; +declare function clear(): Promise; +declare function log(message: string, type?: LoggerType): Promise; +export interface OpenActionOptions { + url: string; +} +export interface RestartOptions { + args: string; +} +declare function exit(code?: number): Promise; +declare function killProcess(): Promise; +declare function restartProcess(options?: RestartOptions): Promise; +declare function getConfig(): Promise; +declare function broadcast(event: string, data?: any): Promise; +declare function readProcessInput(readAll?: boolean): Promise; +declare function writeProcessOutput(data: string): Promise; +declare function writeProcessError(data: string): Promise; +export interface WindowOptions extends WindowSizeOptions, WindowPosOptions { + title?: string; + icon?: string; + fullScreen?: boolean; + alwaysOnTop?: boolean; + enableInspector?: boolean; + borderless?: boolean; + maximize?: boolean; + hidden?: boolean; + maximizable?: boolean; + useSavedState?: boolean; + exitProcessOnClose?: boolean; + extendUserAgentWith?: string; + injectGlobals?: boolean; + injectClientLibrary?: boolean; + injectScript?: string; + processArgs?: string; +} +export interface WindowSizeOptions { + width?: number; + height?: number; + minWidth?: number; + minHeight?: number; + maxWidth?: number; + maxHeight?: number; + resizable?: boolean; +} +export interface WindowPosOptions { + x?: number; + y?: number; + center?: boolean; +} +export interface WindowMenu extends Array { +} +export interface WindowMenuItem { + id?: string; + text: string; + action?: string; + shortcut?: string; + isDisabled?: boolean; + isChecked?: boolean; + menuItems?: WindowMenuItem[]; +} +declare function setTitle(title: string): Promise; +declare function getTitle(): Promise; +declare function maximize(): Promise; +declare function unmaximize(): Promise; +declare function isMaximized(): Promise; +declare function minimize(): Promise; +declare function unminimize(): Promise; +declare function isMinimized(): Promise; +declare function setFullScreen(): Promise; +declare function exitFullScreen(): Promise; +declare function isFullScreen(): Promise; +declare function show(): Promise; +declare function hide(): Promise; +declare function isVisible(): Promise; +declare function focus$1(): Promise; +declare function setIcon(icon: string): Promise; +declare function move$1(x: number, y: number): Promise; +declare function center(): Promise; +declare function beginDrag(screenX?: number, screenY?: number): Promise; +declare function setDraggableRegion(DOMElementOrId: string | HTMLElement, options?: { + exclude?: Array; +}): Promise<{ + success: true; + message: string; + exclusions: { + add(elements: Array): void; + remove(elements: Array): void; + removeAll(): void; + }; +}>; +declare function unsetDraggableRegion(DOMElementOrId: string | HTMLElement): Promise<{ + success: true; + message: string; +}>; +declare function setSize(options: WindowSizeOptions): Promise; +declare function getSize(): Promise; +declare function getPosition(): Promise; +declare function setAlwaysOnTop(onTop: boolean): Promise; +declare function setBorderless(borderless: boolean): Promise; +declare function create(url: string, options?: WindowOptions): Promise; +declare function snapshot(path: string): Promise; +declare function setMainMenu(options: WindowMenu): Promise; +declare function print$1(): Promise; +interface Response$1 { + success: boolean; + message: string; +} +export type Builtin = "ready" | "trayMenuItemClicked" | "windowClose" | "serverOffline" | "clientConnect" | "clientDisconnect" | "appClientConnect" | "appClientDisconnect" | "extClientConnect" | "extClientDisconnect" | "extensionReady" | "neuDev_reloadApp"; +declare function on(event: string, handler: (ev: CustomEvent) => void): Promise; +declare function off(event: string, handler: (ev: CustomEvent) => void): Promise; +declare function dispatch(event: string, data?: any): Promise; +declare function broadcast$1(event: string, data?: any): Promise; +export interface ExtensionStats { + loaded: string[]; + connected: string[]; +} +declare function dispatch$1(extensionId: string, event: string, data?: any): Promise; +declare function broadcast$2(event: string, data?: any): Promise; +declare function getStats$1(): Promise; +export interface Manifest { + applicationId: string; + version: string; + resourcesURL: string; +} +declare function checkForUpdates(url: string): Promise; +declare function install(): Promise; +export interface ClipboardImage { + width: number; + height: number; + bpp: number; + bpr: number; + redMask: number; + greenMask: number; + blueMask: number; + redShift: number; + greenShift: number; + blueShift: number; + data: ArrayBuffer; +} +declare function getFormat(): Promise; +declare function readText(): Promise; +declare function readImage(format?: string): Promise; +declare function writeText(data: string): Promise; +declare function writeImage(image: ClipboardImage): Promise; +declare function readHTML(): Promise; +declare function writeHTML(data: string): Promise; +declare function clear$1(): Promise; +interface Stats$1 { + size: number; + isFile: boolean; + isDirectory: boolean; +} +declare function getFiles(): Promise; +declare function getStats$2(path: string): Promise; +declare function extractFile(path: string, destination: string): Promise; +declare function extractDirectory(path: string, destination: string): Promise; +declare function readFile$1(path: string): Promise; +declare function readBinaryFile$1(path: string): Promise; +declare function mount(path: string, target: string): Promise; +declare function unmount(path: string): Promise; +declare function getMounts(): Promise>; +declare function getMethods(): Promise; +export interface InitOptions { + exportCustomMethods?: boolean; +} +export declare function init(options?: InitOptions): void; +export type ErrorCode = "NE_FS_DIRCRER" | "NE_FS_RMDIRER" | "NE_FS_FILRDER" | "NE_FS_FILWRER" | "NE_FS_FILRMER" | "NE_FS_NOPATHE" | "NE_FS_COPYFER" | "NE_FS_MOVEFER" | "NE_OS_INVMSGA" | "NE_OS_INVKNPT" | "NE_ST_INVSTKY" | "NE_ST_STKEYWE" | "NE_RT_INVTOKN" | "NE_RT_NATPRME" | "NE_RT_APIPRME" | "NE_RT_NATRTER" | "NE_RT_NATNTIM" | "NE_CL_NSEROFF" | "NE_EX_EXTNOTC" | "NE_UP_CUPDMER" | "NE_UP_CUPDERR" | "NE_UP_UPDNOUF" | "NE_UP_UPDINER"; +interface Error$1 { + code: ErrorCode; + message: string; +} +declare global { + interface Window { + /** Mode of the application: window, browser, cloud, or chrome */ + NL_MODE: Mode; + /** Application port */ + NL_PORT: number; + /** Command-line arguments */ + NL_ARGS: string[]; + /** Basic authentication token */ + NL_TOKEN: string; + /** Neutralinojs client version */ + NL_CVERSION: string; + /** Application identifier */ + NL_APPID: string; + /** Application version */ + NL_APPVERSION: string; + /** Application path */ + NL_PATH: string; + /** Application data path */ + NL_DATAPATH: string; + /** Returns true if extensions are enabled */ + NL_EXTENABLED: boolean; + /** Returns true if the client library is injected */ + NL_GINJECTED: boolean; + /** Returns true if globals are injected */ + NL_CINJECTED: boolean; + /** Operating system name: Linux, Windows, Darwin, FreeBSD, or Uknown */ + NL_OS: OperatingSystem; + /** CPU architecture: x64, arm, itanium, ia32, or unknown */ + NL_ARCH: Architecture; + /** Neutralinojs server version */ + NL_VERSION: string; + /** Current working directory */ + NL_CWD: string; + /** Identifier of the current process */ + NL_PID: string; + /** Source of application resources: bundle or directory */ + NL_RESMODE: string; + /** Release commit of the client library */ + NL_CCOMMIT: string; + /** An array of custom methods */ + NL_CMETHODS: string[]; + } + /** Neutralino global object for custom methods **/ + const Neutralino: any; +} + +declare namespace custom { + export { getMethods }; +} +declare namespace filesystem { + export { appendBinaryFile, appendFile, copy, createDirectory, createWatcher, getAbsolutePath, getJoinedPath, getNormalizedPath, getOpenedFileInfo, getPathParts, getPermissions, getRelativePath, getStats, getUnnormalizedPath, getWatchers, move, openFile, readBinaryFile, readDirectory, readFile, remove, removeWatcher, setPermissions, updateOpenedFile, writeBinaryFile, writeFile }; +} +declare namespace os { + export { execCommand, getEnv, getEnvs, getPath, getSpawnedProcesses, open$1 as open, setTray, showFolderDialog, showMessageBox, showNotification, showOpenDialog, showSaveDialog, spawnProcess, updateSpawnedProcess }; +} +declare namespace computer { + export { getArch, getCPUInfo, getDisplays, getKernelInfo, getMemoryInfo, getMousePosition, getOSInfo }; +} +declare namespace storage { + export { clear, getData, getKeys, removeData, setData }; +} +declare namespace debug { + export { log }; +} +declare namespace app { + export { broadcast, exit, getConfig, killProcess, readProcessInput, restartProcess, writeProcessError, writeProcessOutput }; +} +declare namespace window$1 { + export { beginDrag, center, create, exitFullScreen, focus$1 as focus, getPosition, getSize, getTitle, hide, isFullScreen, isMaximized, isMinimized, isVisible, maximize, minimize, move$1 as move, print$1 as print, setAlwaysOnTop, setBorderless, setDraggableRegion, setFullScreen, setIcon, setMainMenu, setSize, setTitle, show, snapshot, unmaximize, unminimize, unsetDraggableRegion }; +} +declare namespace events { + export { broadcast$1 as broadcast, dispatch, off, on }; +} +declare namespace extensions { + export { broadcast$2 as broadcast, dispatch$1 as dispatch, getStats$1 as getStats }; +} +declare namespace updater { + export { checkForUpdates, install }; +} +declare namespace clipboard { + export { clear$1 as clear, getFormat, readHTML, readImage, readText, writeHTML, writeImage, writeText }; +} +declare namespace resources { + export { extractDirectory, extractFile, getFiles, getStats$2 as getStats, readBinaryFile$1 as readBinaryFile, readFile$1 as readFile }; +} +declare namespace server { + export { getMounts, mount, unmount }; +} + +export { + Error$1 as Error, + Permissions$1 as Permissions, + Response$1 as Response, + app, + clipboard, + computer, + custom, + debug, + events, + extensions, + filesystem, + os, + resources, + server, + storage, + updater, + window$1 as window, +}; + +export as namespace Neutralino; + +export {};