PageSourceSearch

https://pupwife.com/scripts/desktop/citrusSettingsWindow.js

js pupwife.com collected 2026-09-30 15:05:46 UTC 5,959 bytes, 187 lines download raw bytes

1/**
2 * CitrusOS Settings — tabbed window shell (replaces modal overlay on index.html).
3 */
4(function (global) {
5    'use strict';
6
7    var SETTINGS_AUX_ID = 'settings';
8    var CSS_HREF = '/css/citrusSettings.css';
9    var DEFAULT_W = 840;
10    var DEFAULT_H = 624;
11    var activeRoot = null;
12    var tabApi = null;
13
14    function ensureCss() {
15        if (document.querySelector('link[data-citrus-settings-css]')) return;
16        var link = document.createElement('link');
17        link.rel = 'stylesheet';
18        link.href = CSS_HREF;
19        link.setAttribute('data-citrus-settings-css', '1');
20        document.head.appendChild(link);
21    }
22
23    function getPanel() {
24        return document.getElementById('settings-panel');
25    }
26
27    function centerPosition(w, h) {
28        return {
29            x: Math.max(16, Math.round((global.innerWidth - w) / 2)),
30            y: Math.max(16, Math.round((global.innerHeight - h - 72) / 2))
31        };
32    }
33
34    function mountPanel(contentEl) {
35        var panel = getPanel();
36        if (!panel) return;
37
38        activeRoot = contentEl;
39        contentEl.className = 'citrus-settings-window';
40        contentEl.innerHTML = '';
41        contentEl.appendChild(panel);
42
43        panel.style.display = '';
44        panel.classList.add('settings-panel--citrusos');
45
46        if (global.PupwifeSettingsTabs && typeof global.PupwifeSettingsTabs.mount === 'function') {
47            tabApi = global.PupwifeSettingsTabs.mount(panel);
48        }
49
50        if (global.PupwifeSettingsSearch && typeof global.PupwifeSettingsSearch.bind === 'function') {
51            global.PupwifeSettingsSearch.bind(panel, {
52                getTabApi: function () { return tabApi; }
53            });
54        }
55    }
56
57    function onOpen() {
58        if (global.SettingsMenu && typeof global.SettingsMenu.onWindowOpen === 'function') {
59            global.SettingsMenu.onWindowOpen();
60        }
61    }
62
63    function onClose() {
64        if (global.PupwifeTrailPicker && typeof global.PupwifeTrailPicker.close === 'function') {
65            global.PupwifeTrailPicker.close();
66        }
67        if (global.PupwifeCursorPicker && typeof global.PupwifeCursorPicker.close === 'function') {
68            global.PupwifeCursorPicker.close();
69        }
70
71        var panel = getPanel();
72        if (panel && activeRoot && panel.parentNode === activeRoot) {
73            document.body.appendChild(panel);
74            panel.classList.remove('settings-panel--open');
75            panel.style.display = 'none';
76        }
77
78        activeRoot = null;
79        tabApi = null;
80
81        if (global.PupwifeSettingsSearch && typeof global.PupwifeSettingsSearch.clear === 'function') {
82            global.PupwifeSettingsSearch.clear(panel);
83        }
84
85        try {
86            document.dispatchEvent(new CustomEvent('pupwife:settings-closed'));
87        } catch (e) {
88            document.dispatchEvent(new Event('pupwife:settings-closed'));
89        }
90    }
91
92    function open(opts) {
93        opts = opts || {};
94        if (!global.CitrusDesktop || typeof global.CitrusDesktop.openAuxWindow !== 'function') {
95            openModalFallback();
96            return;
97        }
98
99        ensureCss();
100
101        if (global.CitrusStylesPolicy && typeof global.CitrusStylesPolicy.pruneDisallowedHostStyles === 'function') {
102            global.CitrusStylesPolicy.pruneDisallowedHostStyles();
103        }
104
105        var pos = (opts.x != null && opts.y != null)
106            ? { x: opts.x, y: opts.y }
107            : centerPosition(DEFAULT_W, DEFAULT_H);
108        var win = global.CitrusDesktop.openAuxWindow({
109            id: SETTINGS_AUX_ID,
110            label: 'Settings',
111            icon: 'fa-solid fa-sliders',
112            w: DEFAULT_W,
113            h: DEFAULT_H,
114            x: pos.x,
115            y: pos.y,
116            nearChat: false,
117            resizable: false,
118            silent: !!opts.silent,
119            auxClass: 'citrus-window--settings',
120            onMount: function (contentEl) {
121                mountPanel(contentEl);
122            },
123            onClose: onClose
124        });
125
126        if (win && win.contentEl) {
127            if (!activeRoot || !getPanel() || !win.contentEl.contains(getPanel())) {
128                mountPanel(win.contentEl);
129            }
130            onOpen();
131        }
132
133        if (opts.minimized && global.CitrusDesktop && typeof global.CitrusDesktop.minimizeApp === 'function') {
134            global.CitrusDesktop.minimizeApp('aux:' + SETTINGS_AUX_ID);
135        }
136    }
137
138    function restoreSession(entry) {
139        if (!entry) return;
140        open({
141            x: entry.x,
142            y: entry.y,
143            silent: true,
144            minimized: !!entry.minimized
145        });
146    }
147
148    function openModalFallback() {
149        var backdrop = document.getElementById('settings-backdrop');
150        var panel = getPanel();
151        if (!backdrop || !panel) return;
152        backdrop.classList.add('settings-backdrop--open');
153        panel.classList.add('settings-panel--open');
154        onOpen();
155    }
156
157    function close() {
158        if (global.CitrusDesktop && typeof global.CitrusDesktop.closeAuxWindow === 'function') {
159            global.CitrusDesktop.closeAuxWindow(SETTINGS_AUX_ID);
160            return;
161        }
162        openModalFallback();
163        var backdrop = document.getElementById('settings-backdrop');
164        var panel = getPanel();
165        if (backdrop) backdrop.classList.remove('settings-backdrop--open');
166        if (panel) panel.classList.remove('settings-panel--open');
167        onClose();
168    }
169
170    function isOpen() {
171        return !!activeRoot;
172    }
173
174    function setActiveTab(tabId) {
175        if (tabApi && typeof tabApi.setActiveTab === 'function') {
176            tabApi.setActiveTab(tabId);
177        }
178    }
179
180    global.CitrusSettingsWindow = {
181        open: open,
182        close: close,
183        isOpen: isOpen,
184        setActiveTab: setActiveTab,
185        restoreSession: restoreSession
186    };
187})(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.