PageSourceSearch

https://pupwife.com/scripts/daw/dawPageCitrus.js

js pupwife.com collected 2026-09-30 15:06:37 UTC 4,539 bytes, 145 lines download raw bytes

1/**
2 * CitrusOS lifecycle adapter for Virtual Keyboard — MIDI routes to focused window only.
3 */
4(function (global) {
5    'use strict';
6
7    var focusObserver = null;
8    var observedShell = null;
9    var fitTimer = null;
10    var fitResizeObserver = null;
11    var fitScopeEl = null;
12
13    function setMidiForFocus(focused) {
14        if (global.DawEngine) {
15            global.DawEngine.setPhysicalMidiEnabled(!!focused);
16        }
17    }
18
19    function watchWindowFocus(containerEl) {
20        if (!containerEl) return;
21        var shell = containerEl.closest('.citrus-window');
22        if (!shell) {
23            setMidiForFocus(true);
24            return;
25        }
26
27        observedShell = shell;
28        setMidiForFocus(shell.classList.contains('citrus-window--focused'));
29
30        if (focusObserver) {
31            focusObserver.disconnect();
32            focusObserver = null;
33        }
34
35        focusObserver = new MutationObserver(function () {
36            if (!observedShell || !observedShell.isConnected) return;
37            setMidiForFocus(observedShell.classList.contains('citrus-window--focused'));
38        });
39        focusObserver.observe(shell, { attributes: true, attributeFilter: ['class'] });
40    }
41
42    function clearFocusWatch() {
43        if (focusObserver) {
44            focusObserver.disconnect();
45            focusObserver = null;
46        }
47        observedShell = null;
48    }
49
50    function measureDawShellHeight(scopeEl, shell) {
51        var titlebar = shell.querySelector('.citrus-window__titlebar');
52        var root = scopeEl.querySelector ? scopeEl.querySelector('#daw-app-root') : null;
53        if (!titlebar || !root) return 0;
54
55        var contentH = Math.ceil(root.getBoundingClientRect().height);
56        if (!contentH) {
57            contentH = root.offsetHeight || root.scrollHeight;
58        }
59
60        return titlebar.offsetHeight + contentH + 2;
61    }
62
63    function fitCitrusWindow(scopeEl) {
64        if (!scopeEl) return;
65        var shell = scopeEl.closest ? scopeEl.closest('.citrus-window') : null;
66        if (!shell || shell.classList.contains('citrus-window--maximized') ||
67            shell.classList.contains('citrus-window--immersive')) {
68            return;
69        }
70
71        var nextH = measureDawShellHeight(scopeEl, shell);
72        if (!nextH) return;
73
74        var appId = shell.getAttribute('data-app-id');
75
76        if (global.CitrusDesktop && typeof global.CitrusDesktop.setWindowSize === 'function' && appId) {
77            global.CitrusDesktop.setWindowSize(appId, null, Math.max(320, nextH));
78            return;
79        }
80
81        shell.style.height = Math.max(320, nextH) + 'px';
82    }
83
84    function scheduleFitWindow(scopeEl) {
85        if (fitTimer) global.clearTimeout(fitTimer);
86        fitTimer = global.setTimeout(function () {
87            fitTimer = null;
88            global.requestAnimationFrame(function () {
89                global.requestAnimationFrame(function () {
90                    fitCitrusWindow(scopeEl);
91                });
92            });
93        }, 0);
94    }
95
96    function watchContentResize(scopeEl) {
97        if (!scopeEl || typeof global.ResizeObserver !== 'function') return;
98        if (fitResizeObserver) {
99            fitResizeObserver.disconnect();
100            fitResizeObserver = null;
101        }
102        fitScopeEl = scopeEl;
103        var root = scopeEl.querySelector('#daw-app-root');
104        if (!root) return;
105
106        fitResizeObserver = new global.ResizeObserver(function () {
107            scheduleFitWindow(fitScopeEl);
108        });
109        fitResizeObserver.observe(root);
110    }
111
112    function clearContentResizeWatch() {
113        if (fitResizeObserver) {
114            fitResizeObserver.disconnect();
115            fitResizeObserver = null;
116        }
117        fitScopeEl = null;
118    }
119
120    function init(scopeEl) {
121        if (!global.DawPage) return Promise.resolve();
122        return global.DawPage.init(scopeEl).then(function () {
123            watchWindowFocus(scopeEl);
124            watchContentResize(scopeEl);
125            scheduleFitWindow(scopeEl);
126        });
127    }
128
129    function destroy(scopeEl) {
130        if (fitTimer) {
131            global.clearTimeout(fitTimer);
132            fitTimer = null;
133        }
134        clearContentResizeWatch();
135        clearFocusWatch();
136        if (!global.DawPage) return Promise.resolve();
137        return global.DawPage.destroy(scopeEl);
138    }
139
140    global.DawPageCitrus = {
141        init: init,
142        destroy: destroy,
143        scheduleFitWindow: scheduleFitWindow
144    };
145})(typeof window !== 'undefined' ? window : this);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.