commit 2c369d74b1da125ebb05bb1fdb8a2a35e57e3221 Author: nonna Date: Wed Mar 11 00:44:45 2026 +0000 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 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 0000000..d708bc3 Binary files /dev/null and b/www/icon.png differ diff --git a/www/index.html b/www/index.html new file mode 100644 index 0000000..352a0c8 --- /dev/null +++ b/www/index.html @@ -0,0 +1,220 @@ + + + + + + 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 {};