feat(ui): render the scene sharing dialogs and bar from a store

createSceneDialog keeps its builder but publishes the dialog's lines,
fields, buttons and status to a scene sharing store; SceneDialog.vue,
teleported to the body from the scene panels view, draws the same modal
with the same styles and opens it with Cancel focused. The EDIT DETAILS /
SHARE SCENE bar is drawn by that view while sharing offers it. The status
directive is shared with Provider Settings, and the two QA scripts that
drove the old DOM read the new dialog.
This commit is contained in:
Sameh Khamis committed 2026-10-31 18:37:14 -07:00
1 parent fe963a1219
commit 9d9eb267c5
15 files changed
+842 -355

No files matched your search

+8 -1
View File
@@ -28,6 +28,7 @@
"src/ui/views/panels/SdrCard.vue",
"src/ui/views/panels/RadioTuner.vue",
"src/ui/views/panels/LocalePicker.vue",
"src/ui/views/panels/SceneDialog.vue",
"src/ui/localePicker.js",
"src/ui/views/panels/PhoneTabBar.vue",
"src/ui/views/panels/CockpitBriefing.vue",
@@ -76,6 +77,7 @@
"src/ui/stores/cockpit.js",
"src/ui/stores/locale.js",
"src/ui/stores/providerSettings.js",
"src/ui/stores/sceneSharing.js",
"src/ui/stores/location.js",
"src/ui/stores/streetLevel.js",
"src/ui/rail/RailChips.vue",
@@ -84,7 +86,8 @@
"src/ui/rail/rangeValue.js",
"src/ui/rail/classToggle.js",
"src/ui/rail/focusSafeChildren.js",
"src/ui/rail/fieldValue.js"
"src/ui/rail/fieldValue.js",
"src/ui/rail/statusText.js"
],
"external": ["vue", "pinia"]
},
@@ -1004,6 +1007,7 @@
"src/ui/stores/cockpit.js",
"src/ui/stores/locale.js",
"src/ui/stores/providerSettings.js",
"src/ui/stores/sceneSharing.js",
"src/ui/stores/location.js",
"src/ui/stores/display.js"
],
@@ -1118,6 +1122,7 @@
"src/director/authoring.js",
"src/scenes/sharing.js",
"src/ui/sceneSharing.js",
"src/ui/stores/sceneSharing.js",
"src/data/cameraArrival.js",
"src/i18n/index.js",
"src/i18n/locale.js",
@@ -2816,6 +2821,7 @@
"src/director/authoring.js",
"src/scenes/sharing.js",
"src/ui/sceneSharing.js",
"src/ui/stores/sceneSharing.js",
"src/layers/weather/imageryOrder.js",
"src/layers/weather/imageryHost.js",
"src/layers/cyclones/index.js",
@@ -2983,6 +2989,7 @@
"src/ui/stores/cockpit.js",
"src/ui/stores/locale.js",
"src/ui/stores/providerSettings.js",
"src/ui/stores/sceneSharing.js",
"src/ui/stores/location.js",
"src/ui/stores/display.js"
],
+3
View File
@@ -427,6 +427,9 @@ try {
shot.interactions = [];
fields[0].value = JSON.stringify(scene);
fields[1].value = JSON.stringify(shot);
// Typing fires input; the dialog reads its fields from those events.
for (const field of fields)
field.dispatchEvent(new Event('input', { bubbles: true }));
});
await page.click('[data-director-apply-details]');
await page.waitForFunction(
+29 -25
View File
@@ -4,7 +4,7 @@
* file dropped from the desktop, proved in a real browser.
*
* The dialog draws a translated row over the native file input
* (src/ui/sceneSharing.js, fileChooser). The input must still cover the row,
* (src/ui/views/panels/SceneDialog.vue). The input must still cover the row,
* or drops miss it. Drops a file on the row's centre via CDP and expects
* `input.files` to hold it.
*
@@ -15,31 +15,34 @@
* drop selects the file, 1 otherwise.
*/
import http from 'node:http';
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import vue from '@vitejs/plugin-vue';
import puppeteer from 'puppeteer';
import { createServer } from 'vite';
import { VUE_PLUGIN_OPTIONS } from '../build/vueOptions.js';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const TYPES = {
'.js': 'text/javascript',
'.mjs': 'text/javascript',
'.json': 'application/json',
'.html': 'text/html',
};
// The scene panels view draws the dialog; Vite compiles it for the page.
const PAGE = `<!doctype html><html lang="en"><body>
<div id="view"></div>
<script type="module">
import { createApp, nextTick } from 'vue';
import { applicationPinia } from '/src/ui/vueRuntime.js';
import LayerPanelsView from '/src/ui/views/LayerPanelsView.vue';
import { createSceneDialog } from '/src/ui/sceneSharing.js';
window.openDialog = () => {
createApp(LayerPanelsView).use(applicationPinia()).mount('#view');
window.openDialog = async () => {
const dialog = createSceneDialog('Share selected scene', () => {});
const files = dialog.input('Choose data-pack files', '', { type: 'file' });
files.multiple = true;
window.filesInput = files;
// Centre of the visible row (the wrapper's last child).
const row = files.parentElement.lastElementChild;
const r = row.getBoundingClientRect();
dialog.input('Choose data-pack files', '', { type: 'file', multiple: true });
await nextTick();
const input = document.querySelector('[data-director-dialog] input[type=file]');
window.filesInput = input;
// Centre of the visible row (the label's last child).
const r = input.parentElement.lastElementChild.getBoundingClientRect();
return { x: r.x + r.width / 2, y: r.y + r.height / 2 };
};
window.ready = true;
@@ -56,21 +59,21 @@ async function main() {
const dir = await mkdtemp(path.join(tmpdir(), 'qa-scene-dialog-drop-'));
const pack = path.join(dir, 'pack.json');
await writeFile(pack, '{}');
const server = http.createServer(async (req, res) => {
const vite = await createServer({
root: ROOT,
configFile: false,
logLevel: 'error',
plugins: [vue(VUE_PLUGIN_OPTIONS)],
server: { middlewareMode: true, hmr: false },
appType: 'custom',
});
const server = http.createServer((req, res) => {
if (req.url === '/') {
res.writeHead(200, { 'content-type': 'text/html' });
res.end(PAGE);
return;
}
try {
const body = await readFile(path.join(ROOT, req.url));
res.writeHead(200, {
'content-type': TYPES[path.extname(req.url)] || 'text/plain',
});
res.end(body);
} catch {
res.writeHead(404).end();
}
vite.middlewares(req, res, () => res.writeHead(404).end());
});
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
const url = `http://127.0.0.1:${server.address().port}/`;
@@ -99,6 +102,7 @@ async function main() {
} finally {
await browser.close();
server.close();
await vite.close();
await rm(dir, { recursive: true, force: true });
}
}