@servicenow/aiux 22.42.1 → 22.42.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.
- package/components/dist/vN/{aiel-c1rnJf9J.js → aiel-Drv2KiHy.js} +3 -3
- package/components/dist/vN/{aiex-BOLhPDhB.js → aiex-Dh5lMLmr.js} +29 -38
- package/components/dist/vN/manifest.json +4 -4
- package/components/dist/vN/{nav-BDWkv54f.js → nav-BatrrdNH.js} +3 -3
- package/components/nav/src/constants.js +2 -1
- package/components/nav/src/nav-layout/src/nav-layout.js +2 -0
- package/components/nav/vN/index.js +4 -2
- package/components/package.json +3 -3
- package/core/commons/package.json +1 -1
- package/core/sdk/dist/build.browser.js +38 -9
- package/core/sdk/package.json +1 -1
- package/core/sdk/src/build-metadata-records/lifecycle.js +7 -3
- package/core/sdk/src/build-runtime-manifest/build.js +46 -6
- package/libraries/aiux-devtools/package.json +1 -1
- package/libraries/aiux-devtools/src/index.js +55 -5
- package/libraries/aiux-devtools/src/store/preferences.js +2 -0
- package/libraries/aiux-devtools/src/ui/shell/devtools-panel.js +80 -43
- package/libraries/aiux-devtools/src/ui/shell/overlay-manager.js +35 -7
- package/package.json +1 -1
- package/public-dev-server/server/package.json +1 -1
- package/public-dev-server/server/src/glide-proxy.js +26 -3
- package/public-dev-server/server/src/server.js +15 -0
|
@@ -313318,6 +313318,17 @@ async function buildPages({
|
|
|
313318
313318
|
}
|
|
313319
313319
|
}
|
|
313320
313320
|
const lifecycleBundle = applicationFile ? entryMap["application"] : null;
|
|
313321
|
+
let lifecycleRequiredTranslations = [];
|
|
313322
|
+
if (applicationFile) {
|
|
313323
|
+
const { requiredTranslations: appTranslations } = analyzePageSource(applicationFile);
|
|
313324
|
+
lifecycleRequiredTranslations = [...appTranslations];
|
|
313325
|
+
if (lifecycleBundle) {
|
|
313326
|
+
lifecycleRequiredTranslations = mergeRequiredTranslations([
|
|
313327
|
+
lifecycleRequiredTranslations,
|
|
313328
|
+
collectEntryOwnTranslations(lifecycleBundle, chunksByFileName)
|
|
313329
|
+
]);
|
|
313330
|
+
}
|
|
313331
|
+
}
|
|
313321
313332
|
const serverDir = path_browser_shim_default.join(distDir, "server");
|
|
313322
313333
|
if (IS_KARUNA_META_PACKAGE_MODE && fs_browser_shim_default.existsSync(serverDir)) {
|
|
313323
313334
|
const codemod = rewriteDir(serverDir, fs_browser_shim_default);
|
|
@@ -313541,13 +313552,27 @@ async function buildPages({
|
|
|
313541
313552
|
...Object.keys(chunkRequiredTranslations).length > 0 ? { chunkRequiredTranslations } : {},
|
|
313542
313553
|
...appDefaultLayout ? { applicationLayout: appDefaultLayout } : {},
|
|
313543
313554
|
...lifecycleBundle ? { lifecycleBundle } : {},
|
|
313555
|
+
...lifecycleRequiredTranslations.length > 0 ? { lifecycleRequiredTranslations } : {},
|
|
313544
313556
|
...extLifecycleBundles.length > 0 ? {
|
|
313545
|
-
extensionLifecycleBundles: extLifecycleBundles.map((e) =>
|
|
313546
|
-
|
|
313547
|
-
|
|
313548
|
-
|
|
313549
|
-
|
|
313550
|
-
|
|
313557
|
+
extensionLifecycleBundles: extLifecycleBundles.map((e) => {
|
|
313558
|
+
const { requiredTranslations: extTranslations } = analyzePageSource(
|
|
313559
|
+
e.file
|
|
313560
|
+
);
|
|
313561
|
+
let extRequiredTranslations = [...extTranslations];
|
|
313562
|
+
if (e.bundle) {
|
|
313563
|
+
extRequiredTranslations = mergeRequiredTranslations([
|
|
313564
|
+
extRequiredTranslations,
|
|
313565
|
+
collectEntryOwnTranslations(e.bundle, chunksByFileName)
|
|
313566
|
+
]);
|
|
313567
|
+
}
|
|
313568
|
+
return {
|
|
313569
|
+
scope: e.scope,
|
|
313570
|
+
host: e.host,
|
|
313571
|
+
...e.experienceSysId ? { experienceSysId: e.experienceSysId } : {},
|
|
313572
|
+
bundle: e.bundle,
|
|
313573
|
+
...extRequiredTranslations.length > 0 ? { requiredTranslations: extRequiredTranslations } : {}
|
|
313574
|
+
};
|
|
313575
|
+
})
|
|
313551
313576
|
} : {},
|
|
313552
313577
|
...requiresCheckManifestPath ? { requiresCheck: requiresCheckManifestPath } : {},
|
|
313553
313578
|
...appRequirements || appFileRequirements ? {
|
|
@@ -315655,7 +315680,10 @@ async function buildLifecycleRecord({ distDir, manifest, shared }) {
|
|
|
315655
315680
|
required_plugins: "[]",
|
|
315656
315681
|
required_user_preferences: "[]",
|
|
315657
315682
|
category: "internal",
|
|
315658
|
-
purpose: "other"
|
|
315683
|
+
purpose: "other",
|
|
315684
|
+
required_translations: JSON.stringify(
|
|
315685
|
+
manifest.lifecycleRequiredTranslations || []
|
|
315686
|
+
)
|
|
315659
315687
|
}
|
|
315660
315688
|
});
|
|
315661
315689
|
const chunks = extractChunkImports(component2);
|
|
@@ -315676,7 +315704,7 @@ async function buildLifecycleRecord({ distDir, manifest, shared }) {
|
|
|
315676
315704
|
return { record, lifecycleBundle };
|
|
315677
315705
|
}
|
|
315678
315706
|
async function buildExtensionLifecycleRecord({ distDir, entry, shared }) {
|
|
315679
|
-
const { bundle, scope, host, experienceSysId } = entry;
|
|
315707
|
+
const { bundle, scope, host, experienceSysId, requiredTranslations } = entry;
|
|
315680
315708
|
if (!bundle) return { record: null };
|
|
315681
315709
|
const { component: component2, sourcemap } = readBundleAndSourcemap(distDir, bundle);
|
|
315682
315710
|
const keySuffix = experienceSysId || `${scope}-${host}`;
|
|
@@ -315700,7 +315728,8 @@ async function buildExtensionLifecycleRecord({ distDir, entry, shared }) {
|
|
|
315700
315728
|
required_plugins: "[]",
|
|
315701
315729
|
required_user_preferences: "[]",
|
|
315702
315730
|
category: "internal",
|
|
315703
|
-
purpose: "other"
|
|
315731
|
+
purpose: "other",
|
|
315732
|
+
required_translations: JSON.stringify(requiredTranslations || [])
|
|
315704
315733
|
}
|
|
315705
315734
|
});
|
|
315706
315735
|
const chunks = extractChunkImports(component2);
|
package/core/sdk/package.json
CHANGED
|
@@ -59,7 +59,10 @@ export async function buildLifecycleRecord({distDir, manifest, shared}) {
|
|
|
59
59
|
required_plugins: '[]',
|
|
60
60
|
required_user_preferences: '[]',
|
|
61
61
|
category: 'internal',
|
|
62
|
-
purpose: 'other'
|
|
62
|
+
purpose: 'other',
|
|
63
|
+
required_translations: JSON.stringify(
|
|
64
|
+
manifest.lifecycleRequiredTranslations || []
|
|
65
|
+
)
|
|
63
66
|
}
|
|
64
67
|
});
|
|
65
68
|
|
|
@@ -91,7 +94,7 @@ export async function buildLifecycleRecord({distDir, manifest, shared}) {
|
|
|
91
94
|
* @returns {{record: object|null}}
|
|
92
95
|
*/
|
|
93
96
|
export async function buildExtensionLifecycleRecord({distDir, entry, shared}) {
|
|
94
|
-
const {bundle, scope, host, experienceSysId} = entry;
|
|
97
|
+
const {bundle, scope, host, experienceSysId, requiredTranslations} = entry;
|
|
95
98
|
if (!bundle) return {record: null};
|
|
96
99
|
|
|
97
100
|
const {component, sourcemap} = readBundleAndSourcemap(distDir, bundle);
|
|
@@ -120,7 +123,8 @@ export async function buildExtensionLifecycleRecord({distDir, entry, shared}) {
|
|
|
120
123
|
required_plugins: '[]',
|
|
121
124
|
required_user_preferences: '[]',
|
|
122
125
|
category: 'internal',
|
|
123
|
-
purpose: 'other'
|
|
126
|
+
purpose: 'other',
|
|
127
|
+
required_translations: JSON.stringify(requiredTranslations || [])
|
|
124
128
|
}
|
|
125
129
|
});
|
|
126
130
|
|
|
@@ -3113,6 +3113,22 @@ async function buildPages({
|
|
|
3113
3113
|
|
|
3114
3114
|
const lifecycleBundle = applicationFile ? entryMap['application'] : null;
|
|
3115
3115
|
|
|
3116
|
+
// Extract required translations from application.js
|
|
3117
|
+
let lifecycleRequiredTranslations = [];
|
|
3118
|
+
if (applicationFile) {
|
|
3119
|
+
const {requiredTranslations: appTranslations} =
|
|
3120
|
+
analyzePageSource(applicationFile);
|
|
3121
|
+
lifecycleRequiredTranslations = [...appTranslations];
|
|
3122
|
+
|
|
3123
|
+
// Recover strings from components imported by only the lifecycle bundle
|
|
3124
|
+
if (lifecycleBundle) {
|
|
3125
|
+
lifecycleRequiredTranslations = mergeRequiredTranslations([
|
|
3126
|
+
lifecycleRequiredTranslations,
|
|
3127
|
+
collectEntryOwnTranslations(lifecycleBundle, chunksByFileName)
|
|
3128
|
+
]);
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
|
|
3116
3132
|
// Karuna meta-package codemod: rewrite bare-specifier imports of
|
|
3117
3133
|
// @servicenow/aiux-* in the compiled SSR output to @servicenow/karuna
|
|
3118
3134
|
// subpath imports. This lets Node's standard ESM resolver locate
|
|
@@ -3478,14 +3494,38 @@ async function buildPages({
|
|
|
3478
3494
|
: {}),
|
|
3479
3495
|
...(appDefaultLayout ? {applicationLayout: appDefaultLayout} : {}),
|
|
3480
3496
|
...(lifecycleBundle ? {lifecycleBundle} : {}),
|
|
3497
|
+
...(lifecycleRequiredTranslations.length > 0
|
|
3498
|
+
? {lifecycleRequiredTranslations}
|
|
3499
|
+
: {}),
|
|
3481
3500
|
...(extLifecycleBundles.length > 0
|
|
3482
3501
|
? {
|
|
3483
|
-
extensionLifecycleBundles: extLifecycleBundles.map(e =>
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3502
|
+
extensionLifecycleBundles: extLifecycleBundles.map(e => {
|
|
3503
|
+
// Extract translations from each extension lifecycle file
|
|
3504
|
+
const {requiredTranslations: extTranslations} = analyzePageSource(
|
|
3505
|
+
e.file
|
|
3506
|
+
);
|
|
3507
|
+
let extRequiredTranslations = [...extTranslations];
|
|
3508
|
+
|
|
3509
|
+
// Recover strings from components imported by only this extension lifecycle bundle
|
|
3510
|
+
if (e.bundle) {
|
|
3511
|
+
extRequiredTranslations = mergeRequiredTranslations([
|
|
3512
|
+
extRequiredTranslations,
|
|
3513
|
+
collectEntryOwnTranslations(e.bundle, chunksByFileName)
|
|
3514
|
+
]);
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
return {
|
|
3518
|
+
scope: e.scope,
|
|
3519
|
+
host: e.host,
|
|
3520
|
+
...(e.experienceSysId
|
|
3521
|
+
? {experienceSysId: e.experienceSysId}
|
|
3522
|
+
: {}),
|
|
3523
|
+
bundle: e.bundle,
|
|
3524
|
+
...(extRequiredTranslations.length > 0
|
|
3525
|
+
? {requiredTranslations: extRequiredTranslations}
|
|
3526
|
+
: {})
|
|
3527
|
+
};
|
|
3528
|
+
})
|
|
3489
3529
|
}
|
|
3490
3530
|
: {}),
|
|
3491
3531
|
...(requiresCheckManifestPath
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicenow/aiux-devtools",
|
|
3
|
-
"version": "1.12.
|
|
3
|
+
"version": "1.12.2",
|
|
4
4
|
"description": "Runtime devtools for AIUX Lit-based widgets — inspect widget trees, properties, lifecycle timing, events, console output, and network requests",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -166,7 +166,11 @@ export async function openPictureInPictureWindow() {
|
|
|
166
166
|
try {
|
|
167
167
|
pipWin = await window.documentPictureInPicture.requestWindow({
|
|
168
168
|
width: 1200,
|
|
169
|
-
height: 700
|
|
169
|
+
height: 700,
|
|
170
|
+
// "back to tab" and close fire the identical pagehide event, so we
|
|
171
|
+
// can't treat them differently — hide it, leaving one unambiguous
|
|
172
|
+
// close; the in-panel dock dropdown is what redocks instead.
|
|
173
|
+
disallowReturnToOpener: true
|
|
170
174
|
});
|
|
171
175
|
} catch (err) {
|
|
172
176
|
// Rejected (no transient activation, blocked by Permissions Policy, or
|
|
@@ -234,6 +238,11 @@ export async function openPictureInPictureWindow() {
|
|
|
234
238
|
return null;
|
|
235
239
|
}
|
|
236
240
|
|
|
241
|
+
// Remember that PiP is the last-used surface, so an inspect-widget request
|
|
242
|
+
// (or re-open) after the PiP window is closed restores PiP rather than
|
|
243
|
+
// falling back to the docked panel.
|
|
244
|
+
preferences.set({detached: true});
|
|
245
|
+
|
|
237
246
|
// Suppress the in-page overlay while the PiP window owns the panel.
|
|
238
247
|
overlayManager.setDetachedActive(true);
|
|
239
248
|
overlayManager.hide();
|
|
@@ -246,14 +255,26 @@ export async function openPictureInPictureWindow() {
|
|
|
246
255
|
}
|
|
247
256
|
|
|
248
257
|
/**
|
|
249
|
-
*
|
|
250
|
-
*
|
|
258
|
+
* Handle the PiP window closing. Closes devtools by default; re-shows the
|
|
259
|
+
* in-page panel only when overlayManager.markRedockPending() was set first
|
|
260
|
+
* (the dock dropdown's position picks) — see the comment below.
|
|
251
261
|
*/
|
|
252
262
|
function _redockFromPip() {
|
|
253
263
|
_pipWindowRef = null;
|
|
254
264
|
_pipPageHideHandler = null;
|
|
255
265
|
detachedHostApi.uninstall();
|
|
256
266
|
overlayManager.setDetachedActive(false);
|
|
267
|
+
|
|
268
|
+
// Default: any popup close fully closes devtools. Exception: picking a
|
|
269
|
+
// dock position marks redockPending before calling window.close(), so
|
|
270
|
+
// the panel returns docked instead of staying hidden.
|
|
271
|
+
//
|
|
272
|
+
// Closing the PiP window (no redock) leaves the `detached` preference set,
|
|
273
|
+
// so a subsequent inspect-widget request reopens in PiP. Only an explicit
|
|
274
|
+
// redock to a dock position switches the remembered surface back to docked.
|
|
275
|
+
if (!overlayManager.consumeRedockPending()) return;
|
|
276
|
+
|
|
277
|
+
preferences.set({detached: false});
|
|
257
278
|
overlayManager.show();
|
|
258
279
|
// Safety net in case the overlay state callback was lost.
|
|
259
280
|
if (_panelElement) {
|
|
@@ -288,6 +309,11 @@ function _destroy() {
|
|
|
288
309
|
if (!_pipWindowRef.closed) _pipWindowRef.close();
|
|
289
310
|
_pipWindowRef = null;
|
|
290
311
|
overlayManager.setDetachedActive(false);
|
|
312
|
+
// The pagehide listener above is what normally consumes redockPending
|
|
313
|
+
// (set by picking a dock position inside PiP, just before this teardown
|
|
314
|
+
// raced ahead of it) — clear it here too, or it would wrongly redock a
|
|
315
|
+
// later, unrelated PiP session's close.
|
|
316
|
+
overlayManager.consumeRedockPending();
|
|
291
317
|
}
|
|
292
318
|
overlayManager.hide();
|
|
293
319
|
// Unmount panel first — triggers disconnectedCallback so panels
|
|
@@ -331,6 +357,25 @@ function _destroy() {
|
|
|
331
357
|
shared.initialized = false;
|
|
332
358
|
}
|
|
333
359
|
|
|
360
|
+
/**
|
|
361
|
+
* Open devtools for an inspect-widget request on the last-used surface.
|
|
362
|
+
*
|
|
363
|
+
* If devtools was last shown in a Picture-in-Picture window (the `detached`
|
|
364
|
+
* preference) and the browser supports PiP, reopen in PiP; otherwise show the
|
|
365
|
+
* docked panel. Must be called from a user-gesture call stack for the PiP
|
|
366
|
+
* branch to satisfy Document PiP's transient-activation requirement.
|
|
367
|
+
*
|
|
368
|
+
* @param {string} widgetId
|
|
369
|
+
*/
|
|
370
|
+
function _openInspectSurface(widgetId) {
|
|
371
|
+
sessionManager.setActiveWidget(widgetId);
|
|
372
|
+
if (preferences.get().detached && 'documentPictureInPicture' in window) {
|
|
373
|
+
openPictureInPictureWindow();
|
|
374
|
+
} else {
|
|
375
|
+
overlayManager.show();
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
334
379
|
/** @type {HTMLElement|null} Active context menu element */
|
|
335
380
|
let _contextMenuEl = null;
|
|
336
381
|
|
|
@@ -407,8 +452,7 @@ function _showContextMenu(x, y, widgetId) {
|
|
|
407
452
|
});
|
|
408
453
|
item.addEventListener('click', () => {
|
|
409
454
|
_dismissContextMenu();
|
|
410
|
-
|
|
411
|
-
overlayManager.show();
|
|
455
|
+
_openInspectSurface(widgetId);
|
|
412
456
|
});
|
|
413
457
|
|
|
414
458
|
menu.appendChild(item);
|
|
@@ -633,6 +677,12 @@ export {registerAdapter, getAdapterFor};
|
|
|
633
677
|
export {toggleDevtools};
|
|
634
678
|
export {openPictureInPictureWindow as openPip};
|
|
635
679
|
|
|
680
|
+
/**
|
|
681
|
+
* Test-only internals. Not part of the public API.
|
|
682
|
+
* @see CLAUDE.md — "Reach internals through the `_testOnly` export".
|
|
683
|
+
*/
|
|
684
|
+
export const _testOnly = {openInspectSurface: _openInspectSurface};
|
|
685
|
+
|
|
636
686
|
// Auto-initialize when loaded in the browser.
|
|
637
687
|
// The full bundle is an ES module loaded by the bootstrap script.
|
|
638
688
|
if (typeof window !== 'undefined' && !getShared().initialized) {
|
|
@@ -106,6 +106,7 @@ export const EXTENDED_EVENT_TYPES = Object.freeze(
|
|
|
106
106
|
* @property {'dark'|'light'} theme - UI theme preference
|
|
107
107
|
* @property {'bottom'|'top'|'left'|'right'} dockPosition - Devtools panel dock position
|
|
108
108
|
* @property {boolean} panelVisible - Whether the devtools panel was open
|
|
109
|
+
* @property {boolean} detached - Whether devtools was last shown in a Picture-in-Picture window (so inspect/re-open restores that surface)
|
|
109
110
|
* @property {boolean} timelineRecording - @deprecated Unified into `recording`. Kept for backward compat.
|
|
110
111
|
* @property {string|null} activeTab - Last selected primary tab
|
|
111
112
|
* @property {string|null} activeSubTab - Last selected inspector sub-tab
|
|
@@ -122,6 +123,7 @@ const DEFAULTS = {
|
|
|
122
123
|
dockPosition: 'bottom',
|
|
123
124
|
timelineRecording: false, // deprecated — unified into `recording`
|
|
124
125
|
panelVisible: false,
|
|
126
|
+
detached: false,
|
|
125
127
|
activeTab: null,
|
|
126
128
|
activeSubTab: null
|
|
127
129
|
};
|
|
@@ -1106,8 +1106,10 @@ export class DevtoolsPanel extends LitElement {
|
|
|
1106
1106
|
}
|
|
1107
1107
|
|
|
1108
1108
|
/**
|
|
1109
|
-
*
|
|
1110
|
-
*
|
|
1109
|
+
* Close the popup/PiP window. By itself this fully closes devtools (the
|
|
1110
|
+
* host's pagehide handler in index.js) — callers that want to redock
|
|
1111
|
+
* instead must call overlayManager.markRedockPending() first (see the
|
|
1112
|
+
* dock-position click handler in _renderDockBtn).
|
|
1111
1113
|
*/
|
|
1112
1114
|
_onDockBack() {
|
|
1113
1115
|
window.close();
|
|
@@ -1424,6 +1426,15 @@ export class DevtoolsPanel extends LitElement {
|
|
|
1424
1426
|
right: svg`<rect x="9.5" y="1.5" width="5" height="11" rx="0.75" fill="currentColor" opacity="0.85"/>`
|
|
1425
1427
|
};
|
|
1426
1428
|
|
|
1429
|
+
/**
|
|
1430
|
+
* Pip icon — Chrome's "undock into separate window" glyph: two overlapping
|
|
1431
|
+
* windows. The front window is a full outline; the back window is drawn as
|
|
1432
|
+
* an open path (top/right/partial-bottom edges only) so it tucks behind the
|
|
1433
|
+
* front without needing a background-colored mask — keeping it correct on
|
|
1434
|
+
* hover and across light/dark themes.
|
|
1435
|
+
*/
|
|
1436
|
+
static _pipIcon = svg`<path d="M5.8,4.2 L5.8,3 Q5.8,1.8 7,1.8 L13,1.8 Q14.2,1.8 14.2,3 L14.2,8.6 Q14.2,9.8 13,9.8 L10.2,9.8" stroke="currentColor" stroke-width="1.2" fill="none" stroke-linejoin="round" stroke-linecap="round"/><rect x="1.8" y="4.2" width="8.4" height="8" rx="1.3" stroke="currentColor" stroke-width="1.2" fill="none"/>`;
|
|
1437
|
+
|
|
1427
1438
|
/** Close the dock overflow menu and remove outside-click listener. */
|
|
1428
1439
|
_closeDockMenu() {
|
|
1429
1440
|
if (!this._dockMenuOpen) return;
|
|
@@ -1467,42 +1478,85 @@ export class DevtoolsPanel extends LitElement {
|
|
|
1467
1478
|
|
|
1468
1479
|
_renderDockBtn(position, activeDock) {
|
|
1469
1480
|
const labels = {bottom: 'bottom', top: 'top', left: 'left', right: 'right'};
|
|
1481
|
+
const isPip = position === 'pip';
|
|
1470
1482
|
return html`
|
|
1471
1483
|
<button
|
|
1472
1484
|
class="dock-btn ${activeDock === position ? 'active' : ''}"
|
|
1473
1485
|
@click=${() => {
|
|
1474
|
-
|
|
1486
|
+
if (isPip) {
|
|
1487
|
+
// Already in PiP when this is the current-state indicator.
|
|
1488
|
+
if (this.mode !== 'detached') this._onPopOut();
|
|
1489
|
+
} else {
|
|
1490
|
+
overlayManager.setDockPosition(position);
|
|
1491
|
+
// Redock (not full close) when picking a position from PiP.
|
|
1492
|
+
if (this.mode === 'detached') {
|
|
1493
|
+
overlayManager.markRedockPending();
|
|
1494
|
+
this._onDockBack();
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1475
1497
|
this._closeDockMenu();
|
|
1476
1498
|
}}
|
|
1477
|
-
title
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1499
|
+
title=${isPip
|
|
1500
|
+
? this.mode === 'detached'
|
|
1501
|
+
? 'Currently in Picture-in-Picture'
|
|
1502
|
+
: 'Undock into separate window'
|
|
1503
|
+
: `Dock to ${labels[position]}`}
|
|
1504
|
+
aria-label=${isPip
|
|
1505
|
+
? this.mode === 'detached'
|
|
1506
|
+
? 'Currently in Picture-in-Picture'
|
|
1507
|
+
: 'Open devtools in a Picture-in-Picture window'
|
|
1508
|
+
: `Dock to ${labels[position]}`}
|
|
1509
|
+
role=${isPip ? 'menuitem' : 'radio'}
|
|
1510
|
+
aria-checked=${isPip
|
|
1511
|
+
? nothing
|
|
1512
|
+
: activeDock === position
|
|
1513
|
+
? 'true'
|
|
1514
|
+
: 'false'}
|
|
1481
1515
|
>
|
|
1482
1516
|
<svg viewBox="0 0 16 14" fill="none">
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1517
|
+
${isPip
|
|
1518
|
+
? DevtoolsPanel._pipIcon
|
|
1519
|
+
: svg`
|
|
1520
|
+
<rect
|
|
1521
|
+
x="1"
|
|
1522
|
+
y="1"
|
|
1523
|
+
width="14"
|
|
1524
|
+
height="12"
|
|
1525
|
+
rx="1.5"
|
|
1526
|
+
stroke="currentColor"
|
|
1527
|
+
stroke-width="1.2"
|
|
1528
|
+
/>
|
|
1529
|
+
${DevtoolsPanel._dockFills[position]}
|
|
1530
|
+
`}
|
|
1493
1531
|
</svg>
|
|
1494
1532
|
</button>
|
|
1495
1533
|
`;
|
|
1496
1534
|
}
|
|
1497
1535
|
|
|
1498
1536
|
/**
|
|
1499
|
-
* Render dock toggle —
|
|
1537
|
+
* Render dock toggle — active icon + dropdown. Pip is a non-radio option
|
|
1538
|
+
* in the same dropdown. While detached, the indicator shows Pip (not the
|
|
1539
|
+
* stale dock position) and the dropdown offers the dock positions instead.
|
|
1540
|
+
* Menu order is fixed: undock (PiP), left, bottom, right, top.
|
|
1500
1541
|
*/
|
|
1501
1542
|
_renderDockToggle(dock) {
|
|
1502
|
-
const
|
|
1543
|
+
const isDetached = this.mode === 'detached';
|
|
1544
|
+
const current = isDetached ? 'pip' : dock;
|
|
1545
|
+
// Canonical dropdown order: undock (PiP) first, then left/bottom/right,
|
|
1546
|
+
// with top last. Pip is only offered when supported and not already
|
|
1547
|
+
// detached; the current surface is shown as the indicator, not repeated
|
|
1548
|
+
// in the menu.
|
|
1549
|
+
const showPip = this._pipSupported && !isDetached;
|
|
1550
|
+
const others = ['pip', 'left', 'bottom', 'right', 'top'].filter(
|
|
1551
|
+
p => p !== current && (p !== 'pip' || showPip)
|
|
1552
|
+
);
|
|
1503
1553
|
return html`
|
|
1504
|
-
<div
|
|
1505
|
-
|
|
1554
|
+
<div
|
|
1555
|
+
class="dock-toggle"
|
|
1556
|
+
role=${isDetached ? nothing : 'radiogroup'}
|
|
1557
|
+
aria-label="Dock position"
|
|
1558
|
+
>
|
|
1559
|
+
${this._renderDockBtn(current, current)}
|
|
1506
1560
|
<div class="dock-more">
|
|
1507
1561
|
<button
|
|
1508
1562
|
class="dock-btn"
|
|
@@ -1521,7 +1575,7 @@ export class DevtoolsPanel extends LitElement {
|
|
|
1521
1575
|
${this._dockMenuOpen
|
|
1522
1576
|
? html`
|
|
1523
1577
|
<div class="dock-menu">
|
|
1524
|
-
${others.map(p => this._renderDockBtn(p,
|
|
1578
|
+
${others.map(p => this._renderDockBtn(p, current))}
|
|
1525
1579
|
</div>
|
|
1526
1580
|
`
|
|
1527
1581
|
: nothing}
|
|
@@ -1606,6 +1660,10 @@ export class DevtoolsPanel extends LitElement {
|
|
|
1606
1660
|
Export
|
|
1607
1661
|
</button>
|
|
1608
1662
|
|
|
1663
|
+
<span class="toolbar-separator"></span>
|
|
1664
|
+
|
|
1665
|
+
${this._renderDockToggle(dock)}
|
|
1666
|
+
|
|
1609
1667
|
<button
|
|
1610
1668
|
class="theme-btn"
|
|
1611
1669
|
@click=${this._onToggleTheme}
|
|
@@ -1618,15 +1676,6 @@ export class DevtoolsPanel extends LitElement {
|
|
|
1618
1676
|
>
|
|
1619
1677
|
${this.theme === 'dark' ? '🌙' : '☀️'}
|
|
1620
1678
|
</button>
|
|
1621
|
-
|
|
1622
|
-
<button
|
|
1623
|
-
class="toolbar-btn"
|
|
1624
|
-
@click=${this._onDockBack}
|
|
1625
|
-
title="Dock back into page"
|
|
1626
|
-
aria-label="Dock back into page"
|
|
1627
|
-
>
|
|
1628
|
-
${svg`<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="2" width="12" height="12" rx="1.5"/><path d="M8 5v6M5 8h6"/></svg>`}
|
|
1629
|
-
</button>
|
|
1630
1679
|
</div>
|
|
1631
1680
|
<div class="inspector-layout">
|
|
1632
1681
|
${this._primaryTab === 'inspector'
|
|
@@ -1738,18 +1787,6 @@ export class DevtoolsPanel extends LitElement {
|
|
|
1738
1787
|
<span class="tab-spacer"></span>
|
|
1739
1788
|
<span class="toolbar-separator"></span>
|
|
1740
1789
|
|
|
1741
|
-
${this._pipSupported
|
|
1742
|
-
? html`
|
|
1743
|
-
<button
|
|
1744
|
-
class="toolbar-btn"
|
|
1745
|
-
@click=${this._onPopOut}
|
|
1746
|
-
title="Open in a Picture-in-Picture window"
|
|
1747
|
-
aria-label="Open devtools in a Picture-in-Picture window"
|
|
1748
|
-
>
|
|
1749
|
-
${svg`<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M9 2h5v5"/><path d="M14 2L7 9"/><path d="M12 9v4.5a.5.5 0 01-.5.5h-9a.5.5 0 01-.5-.5v-9a.5.5 0 01.5-.5H7"/></svg>`}
|
|
1750
|
-
</button>
|
|
1751
|
-
`
|
|
1752
|
-
: nothing}
|
|
1753
1790
|
<button
|
|
1754
1791
|
class="record-btn ${this._globalRecording ? 'recording' : ''}"
|
|
1755
1792
|
@click=${this._onToggleGlobalRecording}
|
|
@@ -15,7 +15,13 @@ import * as preferences from '../../store/preferences.js';
|
|
|
15
15
|
|
|
16
16
|
const _o = getShared().overlay;
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
/** @type {Set<(state: object) => void>} */
|
|
19
|
+
const _stateCallbacks = _o.stateCallbacks;
|
|
20
|
+
|
|
21
|
+
// Restore persisted state. Uses show() rather than a raw `_o.visible = true`
|
|
22
|
+
// — this module can be re-evaluated mid-session (e.g. by a bundler), and a
|
|
23
|
+
// raw write would silently clobber an in-progress PiP session. Must run
|
|
24
|
+
// after _stateCallbacks above: show() -> _notifyState() reads it.
|
|
19
25
|
try {
|
|
20
26
|
const saved = preferences.get();
|
|
21
27
|
if (
|
|
@@ -25,7 +31,7 @@ try {
|
|
|
25
31
|
_o.dockPosition = saved.dockPosition;
|
|
26
32
|
}
|
|
27
33
|
if (saved.panelVisible) {
|
|
28
|
-
|
|
34
|
+
show();
|
|
29
35
|
}
|
|
30
36
|
} catch {
|
|
31
37
|
/* preferences may not be available yet */
|
|
@@ -37,9 +43,6 @@ const MIN_SIZE = 150;
|
|
|
37
43
|
/** @type {number} Max panel size (fraction of viewport) */
|
|
38
44
|
const MAX_SIZE_FRACTION = 0.7;
|
|
39
45
|
|
|
40
|
-
/** @type {Set<(state: object) => void>} */
|
|
41
|
-
const _stateCallbacks = _o.stateCallbacks;
|
|
42
|
-
|
|
43
46
|
function _notifyState() {
|
|
44
47
|
const state = getState();
|
|
45
48
|
// Snapshot the Set — a callback may unsubscribe (delete from the Set)
|
|
@@ -71,7 +74,10 @@ export function getState() {
|
|
|
71
74
|
export function toggle() {
|
|
72
75
|
if (_o.detachedActive) return;
|
|
73
76
|
_o.visible = !_o.visible;
|
|
74
|
-
|
|
77
|
+
// Interacting with the docked panel makes it the last-used surface, so
|
|
78
|
+
// clear the detached (Picture-in-Picture) preference — a later inspect
|
|
79
|
+
// should reopen docked, not in PiP.
|
|
80
|
+
preferences.set({panelVisible: _o.visible, detached: false});
|
|
75
81
|
_notifyState();
|
|
76
82
|
}
|
|
77
83
|
|
|
@@ -82,7 +88,9 @@ export function show() {
|
|
|
82
88
|
if (_o.detachedActive) return;
|
|
83
89
|
if (!_o.visible) {
|
|
84
90
|
_o.visible = true;
|
|
85
|
-
|
|
91
|
+
// Showing the docked panel makes it the last-used surface — clear the
|
|
92
|
+
// detached (PiP) preference so a later inspect reopens docked.
|
|
93
|
+
preferences.set({panelVisible: true, detached: false});
|
|
86
94
|
_notifyState();
|
|
87
95
|
}
|
|
88
96
|
}
|
|
@@ -170,6 +178,25 @@ export function onStateChange(callback) {
|
|
|
170
178
|
return () => _stateCallbacks.delete(callback);
|
|
171
179
|
}
|
|
172
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Mark that the PiP popup is closing to redock at a picked dock position,
|
|
183
|
+
* not to close devtools outright. Consumed once by `index.js`'s pagehide
|
|
184
|
+
* handler, which otherwise treats any popup close as closing devtools.
|
|
185
|
+
*/
|
|
186
|
+
export function markRedockPending() {
|
|
187
|
+
_o.redockPending = true;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Read and clear the redock-pending flag.
|
|
192
|
+
* @returns {boolean} Whether the popup close should redock vs. fully close.
|
|
193
|
+
*/
|
|
194
|
+
export function consumeRedockPending() {
|
|
195
|
+
const pending = !!_o.redockPending;
|
|
196
|
+
_o.redockPending = false;
|
|
197
|
+
return pending;
|
|
198
|
+
}
|
|
199
|
+
|
|
173
200
|
/**
|
|
174
201
|
* Reset state. Used for teardown/testing.
|
|
175
202
|
*/
|
|
@@ -179,5 +206,6 @@ export function reset() {
|
|
|
179
206
|
_o.panelWidth = 420;
|
|
180
207
|
_o.dockPosition = 'bottom';
|
|
181
208
|
_o.detachedActive = false;
|
|
209
|
+
_o.redockPending = false;
|
|
182
210
|
_stateCallbacks.clear();
|
|
183
211
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicenow/aiux-public-dev-server",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Single-process, main-thread local dev server for AIUX apps. Self-contained: no proprietary server internals. Local dev tool, not the production runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|