Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"formatter": {
"bracketSameLine": true,
"enabled": true,
"selfCloseVoidElements": "always"
"selfCloseVoidElements": "always",
"whitespaceSensitivity": "ignore"
}
},
"javascript": {
Expand Down Expand Up @@ -61,6 +62,15 @@
}
}
}
},
{
"includes": ["src/assets/logo.svg"],
"formatter": {
"enabled": false
},
"linter": {
"enabled": false
}
}
],
"vcs": {
Expand Down
148 changes: 74 additions & 74 deletions bun.lock

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"url": "git+https://github.com/opencor/webapp.git"
},
"type": "module",
"version": "0.20260612.4",
"version": "0.20260616.0",
"engines": {
"bun": ">=1.2.0"
},
Expand Down Expand Up @@ -64,11 +64,11 @@
"xxhash-wasm": "^1.1.0"
},
"devDependencies": {
"@biomejs/biome": "^2.4.16",
"@biomejs/biome": "^2.5.0",
"@electron-toolkit/tsconfig": "^2.0.0",
"@electron-toolkit/utils": "^4.0.0",
"@tailwindcss/postcss": "^4.3.0",
"@tailwindcss/vite": "^4.3.0",
"@tailwindcss/postcss": "^4.3.1",
"@tailwindcss/vite": "^4.3.1",
"@types/node": "^25.9.3",
"@types/plotly.js": "^3.0.10",
"@vitejs/plugin-vue": "^6.0.7",
Expand All @@ -77,7 +77,7 @@
"autoprefixer": "^10.5.0",
"cmake-js": "^8.0.0",
"electron": "^41.5.0",
"electron-builder": "^26.15.2",
"electron-builder": "^26.15.3",
"electron-conf": "^1.3.0",
"electron-updater": "^6.8.9",
"electron-vite": "^5.0.0",
Expand All @@ -87,12 +87,12 @@
"rollup-plugin-visualizer": "^7.0.1",
"stylelint": "^17.13.0",
"stylelint-config-standard": "^40.0.0",
"tailwindcss": "^4.3.0",
"tailwindcss": "^4.3.1",
"tailwindcss-primeui": "^0.6.1",
"tar": "^7.5.16",
"typescript": "^6.0.3",
"unplugin-vue-components": "^32.1.0",
"vite": "^7.3.2",
"vue-tsc": "^3.3.4"
"vue-tsc": "^3.3.5"
}
}
4 changes: 2 additions & 2 deletions src/main/assets/splashscreen.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
</td>
</tr>
<tr>
<td class="copyright">Copyright <span id="copyright" /></td>
<td class="version">Version <span id="version" /></td>
<td class="copyright">Copyright<span id="copyright" /></td>
<td class="version">Version<span id="version" /></td>
</tr>
</table>
<script src="./splashscreen.js"></script>
Expand Down
9 changes: 9 additions & 0 deletions src/renderer/biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@
}
}
}
},
{
"includes": ["src/assets/logo.svg"],
"formatter": {
"enabled": false
},
"linter": {
"enabled": false
}
}
],
"vcs": {
Expand Down
128 changes: 64 additions & 64 deletions src/renderer/bun.lock

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions src/renderer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
},
"./style.css": "./dist/opencor.css"
},
"version": "0.20260612.4",
"version": "0.20260616.0",
"libopencorVersion": "0.20260604.0",
"scripts": {
"build": "vite build && bun scripts/generate.version.js",
Expand Down Expand Up @@ -76,9 +76,9 @@
"xxhash-wasm": "^1.1.0"
},
"devDependencies": {
"@biomejs/biome": "^2.4.16",
"@tailwindcss/postcss": "^4.3.0",
"@tailwindcss/vite": "^4.3.0",
"@biomejs/biome": "^2.5.0",
"@tailwindcss/postcss": "^4.3.1",
"@tailwindcss/vite": "^4.3.1",
"@types/node": "^25.9.3",
"@types/plotly.js": "^3.0.10",
"@vitejs/plugin-vue": "^6.0.7",
Expand All @@ -89,11 +89,11 @@
"rollup-plugin-visualizer": "^7.0.1",
"stylelint": "^17.13.0",
"stylelint-config-standard": "^40.0.0",
"tailwindcss": "^4.3.0",
"tailwindcss": "^4.3.1",
"tailwindcss-primeui": "^0.6.1",
"typescript": "^6.0.3",
"unplugin-vue-components": "^32.1.0",
"vite": "^7.3.2",
"vue-tsc": "^3.3.4"
"vue-tsc": "^3.3.5"
}
}
115 changes: 37 additions & 78 deletions src/renderer/src/common/vueCommon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,99 +100,58 @@ export const trackElementHeight = (
return stopTrackingElementHeight;
};

// Hybrid append target strategy for PrimeVue overlays:
// - Default: append to `document.body`. This avoids containing-block issues from CSS ancestors (e.g.,
// `position: relative` or `overflow: hidden`) that would clip the overlay. Even if an ancestor has
// `overflow: hidden`, the fixed container gets clipped entirely.
// - Full-screen fallback: when a non-`body` element enters full-screen mode, `body`-level overlays become hidden
// behind the full-screen container. In that case, we fall back to a `position: fixed` container inside `.opencor`
// (which is a descendant of the full-screen element). The `getBoundingClientRect()`-based correction handles both
// plain scroll offsets and any containing-block shifts from CSS ancestors.

export const useAppendTarget = () => {
// A composable that provides an overlay container as an append target for PrimeVue overlays. PrimeVue's
// `absolutePosition()` computes document-absolute coordinates (viewport-relative `getBoundingClientRect()` plus
// `windowScrollTop`/`windowScrollLeft`). The container uses `position: fixed` inside `.opencor`, and its `top`/`left`
// are dynamically negated by the current scroll offset (`-scrollY`/`-scrollX`). This converts PrimeVue's
// document-absolute coordinates back to viewport-relative, so overlays appear at the correct screen position regardless
// of the host app's scroll state.
//
// Keeping the container inside `.opencor` also ensures that overlays are visible when the host app uses full-screen
// mode (the Fullscreen API only renders the full-screen element and its CSS descendants), and that they inherit
// `.opencor`'s CSS properties through the DOM tree.

export const useAppendTarget = (ancestorRef: vue.Ref<HTMLElement | null>) => {
const appendTarget = vue.shallowRef<HTMLElement | undefined>(undefined);
const containerClass = 'opencor-overlay-container';

// Lazily create the fixed container inside `.opencor` (only needed for full-screen fallback).

const containerInsideOpencor = (instance: vue.ComponentInternalInstance | null): HTMLElement | undefined => {
const rootEl = instance?.vnode?.el;
const opencor = rootEl instanceof Element ? rootEl.closest('.opencor') : document.querySelector('.opencor');

if (!opencor) {
return undefined;
}

let overlayContainer = opencor.querySelector(`.${containerClass}`) as HTMLElement | null;

if (!overlayContainer) {
overlayContainer = document.createElement('div');

overlayContainer.className = containerClass;
overlayContainer.style.cssText =
'position: fixed; top: 0; left: 0; width: 0; height: 0; overflow: visible; pointer-events: none; z-index: 99999;';
vue.onMounted(() => {
const opencor = ancestorRef.value?.closest('.opencor');

// Restore pointer events for overlay content teleported into the container.
if (opencor) {
let container = opencor.querySelector(`.${containerClass}`) as HTMLElement | null;

overlayContainer.appendChild(
Object.assign(document.createElement('style'), {
textContent: `.${containerClass} > * { pointer-events: auto; }`
})
);
if (!container) {
const divElement = document.createElement('div');

opencor.appendChild(overlayContainer);
divElement.className = containerClass;
divElement.style.cssText =
'position: fixed; top: 0; left: 0; width: 0; height: 0; overflow: visible; pointer-events: none; z-index: 99999;';

const container = overlayContainer;
const updateScrollOffset = () => {
const rect = container.getBoundingClientRect();
const oldTop = parseFloat(container.style.top) || 0;
const oldLeft = parseFloat(container.style.left) || 0;
const newTop = oldTop - rect.top - window.scrollY;
const newLeft = oldLeft - rect.left - window.scrollX;
// Restore pointer events for overlay content teleported into the container.

if (Math.abs(newTop - oldTop) >= 0.5 || Math.abs(newLeft - oldLeft) >= 0.5) {
container.style.top = `${newTop}px`;
container.style.left = `${newLeft}px`;
}
};
divElement.appendChild(
Object.assign(document.createElement('style'), {
textContent: `.${containerClass} > * { pointer-events: auto; }`
})
);

updateScrollOffset();
opencor.appendChild(divElement);

window.addEventListener('scroll', updateScrollOffset, { passive: true });
}

return overlayContainer;
};
const updateOffset = (): void => {
divElement.style.top = `-${window.scrollY}px`;
divElement.style.left = `-${window.scrollX}px`;
};

// Resolve the correct append target based on the full-screen state.
updateOffset();

const resolveAppendTarget = (instance: vue.ComponentInternalInstance | null): HTMLElement => {
const fullscreenEl = document.fullscreenElement;
window.addEventListener('scroll', updateOffset, { passive: true });

// If we're in full-screen mode and the full-screen element doesn't contain `body`, then `body` is hidden behind the
// full-screen container, in which case we use the fixed container inside `.opencor`.
container = divElement;
}

if (fullscreenEl && !fullscreenEl.contains(document.body)) {
return containerInsideOpencor(instance) ?? document.body;
appendTarget.value = container;
}

// In normal mode, we use `document.body` to avoid containing-block/clipping issues.

return document.body;
};

vue.onMounted(() => {
const instance = vue.getCurrentInstance();

// Initial resolution.

appendTarget.value = resolveAppendTarget(instance);

// Re-resolve when full-screen state changes.

document.addEventListener('fullscreenchange', () => {
appendTarget.value = resolveAppendTarget(instance);
});
});

return appendTarget;
Expand Down
Loading
Loading