diff --git a/resources/js/canvas/BetaBadge.svelte b/resources/js/canvas/BetaBadge.svelte
new file mode 100644
index 0000000000..45d949307a
--- /dev/null
+++ b/resources/js/canvas/BetaBadge.svelte
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/resources/js/canvas/deploy/DeployCanvas.svelte b/resources/js/canvas/deploy/DeployCanvas.svelte
index 89dab86f61..14871dcfcd 100644
--- a/resources/js/canvas/deploy/DeployCanvas.svelte
+++ b/resources/js/canvas/deploy/DeployCanvas.svelte
@@ -4,6 +4,7 @@
import Dropdown from './Dropdown.svelte';
import Edges from './Edges.svelte';
import Icon from './Icon.svelte';
+ import BetaBadge from '../BetaBadge.svelte';
import Node from './Node.svelte';
import PaletteItem from './PaletteItem.svelte';
import RunPanel from './RunPanel.svelte';
@@ -82,7 +83,7 @@
{/if}
- Beta
+
{/snippet}
diff --git a/resources/js/canvas/deploy/state.svelte.js b/resources/js/canvas/deploy/state.svelte.js
index 2cfb6c3c24..ffb74cd4ff 100644
--- a/resources/js/canvas/deploy/state.svelte.js
+++ b/resources/js/canvas/deploy/state.svelte.js
@@ -121,6 +121,15 @@ const height = n => els.nodeEls.get(n.id)?.offsetHeight || 160;
// A card's box in world units (its rendered size, or a usual card size).
const boxOf = n => ({ x: n.x, y: n.y, w: width(n), h: height(n) });
+// The part of the viewport that the floating Steps panel does not cover,
+// relative to the viewport (on small screens the panel is above it).
+function openArea() {
+ const r = els.viewport.getBoundingClientRect();
+ const p = els.root.querySelector('.cv-palette').getBoundingClientRect();
+ const left = p.bottom > r.top ? Math.max(0, p.right - r.left) : 0;
+ return { left, width: r.width - left, height: r.height };
+}
+
export function fit() {
if (!els.nodeEls.size) return;
let minX = Infinity; let minY = Infinity; let maxX = -Infinity; let maxY = -Infinity;
@@ -136,11 +145,11 @@ export function fit() {
maxX = Math.max(maxX, pill.offsetLeft + pill.offsetWidth);
maxY = Math.max(maxY, pill.offsetTop + pill.offsetHeight);
}
- const r = els.viewport.getBoundingClientRect();
+ const r = openArea();
const pad = 60;
const view = cv.view;
view.z = Math.min(1.2, Math.max(MIN_ZOOM, Math.min((r.width - pad * 2) / (maxX - minX), (r.height - pad * 2) / (maxY - minY))));
- view.x = (r.width - (maxX - minX) * view.z) / 2 - minX * view.z;
+ view.x = r.left + (r.width - (maxX - minX) * view.z) / 2 - minX * view.z;
view.y = (r.height - (maxY - minY) * view.z) / 2 - minY * view.z;
els.pz.apply();
}
@@ -259,7 +268,8 @@ function appendToChain(type) {
}
function centre() {
const r = els.viewport.getBoundingClientRect();
- const p = toWorld(r.left + r.width / 2, r.top + r.height / 2);
+ const a = openArea();
+ const p = toWorld(r.left + a.left + a.width / 2, r.top + r.height / 2);
return [p.x - 140, p.y - 60];
}
@@ -299,6 +309,7 @@ export function dragFromPalette(type, event) {
ghost.remove();
const r = els.viewport.getBoundingClientRect();
if (e.clientX < r.left || e.clientX > r.right || e.clientY < r.top || e.clientY > r.bottom) return;
+ if (e.target.closest?.('.cv-palette')) return;
const p = toWorld(e.clientX, e.clientY);
addNode(type, p.x - 30, p.y - 16);
};
diff --git a/resources/js/canvas/deploy/styles.js b/resources/js/canvas/deploy/styles.js
index 869e76d236..b05e1fa014 100644
--- a/resources/js/canvas/deploy/styles.js
+++ b/resources/js/canvas/deploy/styles.js
@@ -16,8 +16,8 @@ const CSS = `
--cv-line: var(--coollabs-hairline, light-dark(#e5e5e5, rgb(255 255 255 / 0.08)));
--cv-input-bg: light-dark(#fafafa, rgb(255 255 255 / 0.04));
--cv-input-line: light-dark(#e5e5e5, rgb(255 255 255 / 0.08));
- --cv-brand: light-dark(var(--color-coollabs), var(--color-warning));
- --cv-brand-text: var(--cv-brand);
+ --cv-brand: light-dark(var(--color-coollabs), var(--color-brand));
+ --cv-brand-text: light-dark(var(--color-coollabs), var(--color-brand-light));
position: absolute; inset: 0; z-index: 10;
display: grid; grid-template-columns: 15.5rem minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr);
gap: 8px; padding: 8px;
@@ -100,6 +100,13 @@ const CSS = `
/* Canvas island */
.cv-stage { position: relative; overflow: hidden; border: 0; background: transparent; }
+ /* Wide screens: the canvas fills the whole area to the screen edges and the
+ Steps panel floats over it, so cards slide under the panel, not cut off. */
+ @media (min-width: 768px) {
+ .cv-stage { grid-column: 1 / -1; grid-row: -2 / -1; margin: 0 -8px -8px; }
+ .cv-docked .cv-stage { margin-top: -8px; }
+ .cv-palette { grid-column: 1; grid-row: -2 / -1; position: relative; z-index: 15; box-shadow: 0 16px 40px -20px rgb(0 0 0 / 0.35); }
+ }
.cv-viewport {
position: absolute; inset: 0; cursor: grab; touch-action: none;
/* The dots are on .cv-root, so the viewport has none of its own. */
diff --git a/resources/js/canvas/environment/Toolbar.svelte b/resources/js/canvas/environment/Toolbar.svelte
index ee53dcb078..2ac3fd0766 100644
--- a/resources/js/canvas/environment/Toolbar.svelte
+++ b/resources/js/canvas/environment/Toolbar.svelte
@@ -1,4 +1,5 @@