@simular-ai/simulib-js 5.4.3

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,57 @@
1
+ // Run: node examples/log-window.mjs
2
+ // You can configure logging via RUST_LOG:
3
+ // RUST_LOG=simulib_rs=debug,info node examples/log-window.mjs
4
+ //
5
+ // Pipes every Rust-side `log::*!` record from simulib-rs into a floating,
6
+ // always-on-top log window. The window comes from the sibling package
7
+ // `@simular-ai/simulib-log-viewer`, which is an optional peer dependency
8
+ // — install it explicitly to run this example:
9
+ // npm install @simular-ai/simulib-log-viewer
10
+ //
11
+ // The window is **click-through by default**, so it never interferes with
12
+ // the app you're driving — every mouse click passes straight through. To
13
+ // move it or pause execution, press the global grab hotkey shown inside
14
+ // the window (Ctrl+Shift+Option+L on macOS, Ctrl+Shift+Alt+L elsewhere).
15
+ // Press it once to enter "grab mode" (click-through off, paused, draggable);
16
+ // press it again to resume.
17
+ //
18
+ // Inside an automation loop, `await win.waitIfPaused()` yields cleanly
19
+ // while the user has paused; it returns immediately otherwise.
20
+
21
+ import { Clipboard, screenshotFull, Screen, initLogger } from '@simular-ai/simulib-js'
22
+ import { LogWindow } from '@simular-ai/simulib-log-viewer'
23
+
24
+ const win = new LogWindow()
25
+ win.spawn()
26
+
27
+ // `'info'` mirrors `RUST_LOG=info`. Use e.g. `'simulib_rs=debug,warn'` to
28
+ // scope per-module. Omit the second argument to fall back to `RUST_LOG`.
29
+ initLogger((rec) => {
30
+ const line = `[${rec.level}] [${rec.target}] ${rec.message}`
31
+ console.log(line)
32
+ win.log(line)
33
+ }, 'info')
34
+
35
+ // Run a few simulib-js calls so there's actually something to look at.
36
+ win.log('--- starting clipboard demo ---')
37
+ try {
38
+ const cb = new Clipboard()
39
+ const previous = cb.setString('hello from simulib-js')
40
+ win.log(`clipboard now contains: ${cb.getString()}`)
41
+ if (previous !== null) cb.setString(previous)
42
+
43
+ win.log('--- starting screenshot demo ---')
44
+ const shot = screenshotFull(true, Screen.mainScreen())
45
+ shot.shrink(640, 480)
46
+ win.log(`captured ${shot.base64().length} bytes (base64)`)
47
+ } catch (err) {
48
+ win.log(`demo failed: ${err instanceof Error ? err.message : String(err)}`)
49
+ }
50
+
51
+ win.log('done. Holding window open for 30s — press the grab hotkey to pause.')
52
+ await new Promise((resolve) => setTimeout(resolve, 30_000))
53
+ // In real automation, scatter `await win.waitIfPaused()` between actions
54
+ // so the script yields whenever the user has grabbed the window.
55
+ await win.waitIfPaused()
56
+
57
+ win.close()
@@ -0,0 +1,33 @@
1
+ // Run: node examples/logger.mjs
2
+ // Or with a per-module spec:
3
+ // RUST_LOG=simulib_rs::windows=debug,warn node examples/logger.mjs
4
+ //
5
+ // `initLogger()` accepts an optional JS callback. Without one, records go to
6
+ // stderr (analogous to the env_logger crate's default). With a callback, each record is forwarded
7
+ // from any Rust thread to your sink — useful for routing logs into a GUI
8
+ // panel, a file appender, pino/winston, or an Electron renderer over IPC.
9
+ //
10
+ // This example shows the callback form. For the env_logger-style default
11
+ // sink, just call `initLogger()` with no arguments.
12
+
13
+ import { initLogger, readFile } from '@simular-ai/simulib-js'
14
+
15
+ initLogger((record) => {
16
+ // Records carry { level, target, message, file?, line?, modulePath? }.
17
+ // Route however you like — here we just prefix and forward to console.
18
+ console.log(`[${record.level}] [${record.target}] ${record.message}`)
19
+ })
20
+
21
+ // Trigger something so we have at least one record to look at.
22
+ try {
23
+ readFile('/tmp/this/file/probably/does/not/exist')
24
+ } catch {
25
+ // expected — we just want to exercise the code paths that may log.
26
+ }
27
+
28
+ // The JS callback dispatch is unref'd (so the logger never keeps Node alive
29
+ // on its own). For a short-lived script that does its work and exits
30
+ // immediately, that means pending records may be dropped. A long-running
31
+ // app like an Electron GUI or a server doesn't need this; here we yield to
32
+ // libuv once so the dispatch queue can drain before exit.
33
+ await new Promise((resolve) => setImmediate(resolve))
@@ -0,0 +1,41 @@
1
+ // Run: node examples/loopback.mjs <audio-file>
2
+ //
3
+ // Plays an audio file through the speakers while capturing system audio
4
+ // via loopback, then transcribes the captured audio using Whisper.
5
+ //
6
+ // Requires screen-recording permission on macOS (for loopback capture).
7
+
8
+ import { AudioOutput, LoopbackSource, SttModel } from '@simular-ai/simulib-js'
9
+
10
+ const audioFile = process.argv[2]
11
+ if (!audioFile) {
12
+ console.error('Usage: node examples/loopback.mjs <audio-file>')
13
+ process.exit(1)
14
+ }
15
+
16
+ try {
17
+ const loopback = new LoopbackSource(2, 48000)
18
+ loopback.start()
19
+
20
+ const output = AudioOutput.openDefault()
21
+ const player = output.createPlayer()
22
+
23
+ console.log(`Playing ${audioFile} to speakers ...`)
24
+ player.appendFile(audioFile)
25
+ player.sleepUntilEnd()
26
+
27
+ // Give loopback a moment to flush any trailing samples.
28
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 500)
29
+ loopback.stop()
30
+
31
+ console.log('Captured loopback audio, draining ...')
32
+ const audio = loopback.drain()
33
+ console.log(` ${audio.durationMs.toFixed(0)} ms, ${audio.channels} ch, ${audio.sampleRate} Hz`)
34
+
35
+ console.log('Transcribing ...')
36
+ const model = SttModel.whisperLargeV3Turbo()
37
+ const text = model.transcribe(audio)
38
+ console.log(`Transcription: "${text}"`)
39
+ } catch (error) {
40
+ console.error('Loopback failed:', error instanceof Error ? error.message : error)
41
+ }
@@ -0,0 +1,68 @@
1
+ // Run: node examples/open-app.mjs [appName]
2
+ // Tests the full pipeline: find app -> open -> get PID -> bind tree -> snapshot.
3
+
4
+ import {
5
+ System,
6
+ AccessibilityTree,
7
+ FocusPolicy,
8
+ Visibility,
9
+ TraversalOrder,
10
+ AriaRole,
11
+ Window,
12
+ } from '@simular-ai/simulib-js'
13
+
14
+ const appName = process.argv[2] || 'Chrome'
15
+
16
+ // 1. List installed apps
17
+ console.log('=== Installed apps (first 20) ===')
18
+ const apps = System.listApps()
19
+ apps.slice(0, 20).forEach((a) => console.log(` ${a.canonicalName} -> ${a.launchTarget}`))
20
+ console.log(` ... (${apps.length} total)\n`)
21
+
22
+ // 2. Fuzzy search
23
+ console.log(`=== Searching for: ${appName} ===`)
24
+ const app = System.fuzzySearch(appName)
25
+ console.log(`Found: ${app.canonicalName} -> ${app.launchTarget}\n`)
26
+
27
+ // 3. Open the app
28
+ console.log(`=== Opening ${appName} ===`)
29
+ const instance = app.open('https://www.google.com', FocusPolicy.Steal, Visibility.Show, true)
30
+ console.log('PID:', instance.pid)
31
+ console.log()
32
+
33
+ if (instance.pid === 0) {
34
+ console.log('WARNING: PID is 0 - ShellExecuteEx did not return a process handle')
35
+ process.exit(1)
36
+ }
37
+
38
+ // 4. List windows for this PID
39
+ console.log(`=== Windows for PID ${instance.pid} ===`)
40
+ const windows = Window.allForPid(instance.pid)
41
+ windows.forEach((w) => console.log(` pid=${w.pid} "${w.title}"`))
42
+ console.log()
43
+
44
+ // 5. Bind and snapshot
45
+ if (windows.length > 0) {
46
+ console.log(`=== Snapshot of ${appName} ===`)
47
+ const tree = AccessibilityTree.fromPid(instance.pid)
48
+ console.log(`Bound to: "${tree.windowTitle}"`)
49
+
50
+ const root = tree.snapshot(true)
51
+ console.log('Role:', root.role)
52
+ console.log('Children:', root.children.length)
53
+ console.log('First 10 children:')
54
+ root.children.slice(0, 10).forEach((c) => {
55
+ const name = c.name ? `"${c.name}"` : ''
56
+ console.log(` - ${c.role} ${name} [ref=${c.refId}]`)
57
+ })
58
+
59
+ // 6. Find the first button using depth-first search
60
+ const [firstButton] = tree.find(TraversalOrder.DepthFirst, AriaRole.Button, undefined, true, 1)
61
+ if (firstButton && firstButton.refId != null) {
62
+ console.log(`\nFirst button: "${firstButton.name}" [ref=${firstButton.refId}]`)
63
+ console.log('Actions:', tree.getSupportedActions(firstButton.refId))
64
+ await new Promise((resolve) => setTimeout(resolve, 2000))
65
+ console.log(`Activating: "${firstButton.name}"`)
66
+ tree.activate(firstButton.refId)
67
+ }
68
+ }
@@ -0,0 +1,15 @@
1
+ // Run: node examples/screenshot.mjs
2
+ // This example takes a screenshot, shrinks it, compresses it, and saves it.
3
+
4
+ import { Screen, screenshotFull } from '@simular-ai/simulib-js'
5
+
6
+ try {
7
+ const screenshot = screenshotFull(true, Screen.mainScreen())
8
+ screenshot.shrink(1920, 1080)
9
+ screenshot.compress(80)
10
+ screenshot.save('simulib-js-screenshot.png')
11
+ console.log('Saved simulib-js-screenshot.png')
12
+ console.log('Base64 length:', screenshot.base64().length)
13
+ } catch (error) {
14
+ console.error('Screenshot failed:', error instanceof Error ? error.message : error)
15
+ }
@@ -0,0 +1,15 @@
1
+ import { App, FocusPolicy, Visibility, AccessibilityTree, AriaRole, TraversalOrder } from '@simular-ai/simulib-js'
2
+
3
+ // Open simular.ai in the default browser and wait for it to load
4
+ App.defaultBrowser().open('https://simular.ai', FocusPolicy.Steal, Visibility.Show, true)
5
+
6
+ // Bind to the browser window and find the "About" link by role and name
7
+ const tree = AccessibilityTree.fromForeground()
8
+ const [link] = tree.find(TraversalOrder.BreadthFirst, AriaRole.Link, 'About', true, 1)
9
+
10
+ // Click it
11
+ if (link && link.refId != null) {
12
+ tree.activate(link.refId)
13
+ } else {
14
+ console.error('Could not find the "About" link')
15
+ }
@@ -0,0 +1,10 @@
1
+ // Run: node examples/system.mjs
2
+ // This example opens a URL in the default browser.
3
+
4
+ import { App, FocusPolicy, Visibility } from '@simular-ai/simulib-js'
5
+
6
+ // Open a URL in the default browser.
7
+ App.defaultBrowser().open('https://example.com', FocusPolicy.Steal, Visibility.Show, true)
8
+
9
+ // Open a specific app (macOS example).
10
+ // App.exactName('Safari').open(null, FocusPolicy.Steal, Visibility.Show, false)