diff --git a/apps/web/src/lib/tests/Canvas.svelte.test.ts b/apps/web/src/lib/tests/Canvas.svelte.test.ts
index 7f763b6..438008a 100644
--- a/apps/web/src/lib/tests/Canvas.svelte.test.ts
+++ b/apps/web/src/lib/tests/Canvas.svelte.test.ts
@@ -127,6 +127,7 @@ describe('Canvas component', () => {
'.application-chrome [aria-label="Open stencils library"]'
);
expect(insertButton).toBeTruthy();
+ expect(insertButton?.querySelector('.i-ph-list-plus')).toBeTruthy();
expect(insertButton?.closest('.toolbar')).toBeNull();
});
diff --git a/packages/ui/src/lib/editor/components/SelectionControls.svelte b/packages/ui/src/lib/editor/components/SelectionControls.svelte
index 2ee6bc6..6123444 100644
--- a/packages/ui/src/lib/editor/components/SelectionControls.svelte
+++ b/packages/ui/src/lib/editor/components/SelectionControls.svelte
@@ -800,20 +800,6 @@
{selectionCount}
{selectionCount === 1 ? 'object' : 'objects'} selected
-
-
-
-
+
+
+
+
+
-
+
Insert
{/if}
diff --git a/packages/ui/src/lib/editor/components/__tests__/SelectionControls.svelte.test.ts b/packages/ui/src/lib/editor/components/__tests__/SelectionControls.svelte.test.ts
index 14c2a03..9f66ef3 100644
--- a/packages/ui/src/lib/editor/components/__tests__/SelectionControls.svelte.test.ts
+++ b/packages/ui/src/lib/editor/components/__tests__/SelectionControls.svelte.test.ts
@@ -51,6 +51,35 @@ describe('SelectionControls', () => {
.not.toBeInTheDocument();
});
+ it('keeps contextual sections on one horizontal viewport with scroll controls', async () => {
+ const page = PageRecord.create('Test page', 'page:test');
+ const rect = ShapeRecord.createRect(
+ page.id,
+ 0,
+ 0,
+ { w: 80, h: 50, fill: '#ffffff', stroke: '#111111', radius: 4 },
+ 'rect'
+ );
+ const screen = render(SelectionControls, {
+ currentTool: 'select',
+ orientation: 'vertical',
+ store: createSelectionStore([rect])
+ });
+ const toolbar = screen.getByRole('toolbar', { name: 'Selection controls' }).element();
+ const sections = toolbar.querySelector('.selection-controls__sections');
+ if (!sections) throw new Error('Expected the contextual sections viewport');
+
+ expect(getComputedStyle(toolbar).display).toBe('flex');
+ expect(getComputedStyle(sections).flexWrap).toBe('nowrap');
+ expect(getComputedStyle(sections).overflowX).toBe('auto');
+ await expect
+ .element(screen.getByRole('button', { name: 'Show previous contextual controls' }))
+ .toBeInTheDocument();
+ await expect
+ .element(screen.getByRole('button', { name: 'Show more contextual controls' }))
+ .toBeInTheDocument();
+ });
+
it('projects and edits semantic metadata for ordinary objects', async () => {
const page = PageRecord.create('Test page', 'page:test');
const rect = ShapeRecord.createRect(
diff --git a/packages/ui/src/lib/icons.ts b/packages/ui/src/lib/icons.ts
index dc87ca1..57060c7 100644
--- a/packages/ui/src/lib/icons.ts
+++ b/packages/ui/src/lib/icons.ts
@@ -25,6 +25,7 @@ export const ICONS = {
'grip-vertical': 'i-tabler-grip-vertical',
history: 'i-tabler-history',
'info-circle': 'i-tabler-info-circle',
+ insert: 'i-ph-list-plus',
light: 'i-tabler-sun',
menu: 'i-bi-list',
markdown: 'i-tabler-markdown',
diff --git a/packages/ui/src/lib/styles/icons.css b/packages/ui/src/lib/styles/icons.css
index 1103450..a958e1b 100644
--- a/packages/ui/src/lib/styles/icons.css
+++ b/packages/ui/src/lib/styles/icons.css
@@ -9,6 +9,7 @@
.i-ph-copy{--un-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' display='inline-block' flex-shrink='0' vertical-align='-0.125em' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M216 32H88a8 8 0 0 0-8 8v40H40a8 8 0 0 0-8 8v128a8 8 0 0 0 8 8h128a8 8 0 0 0 8-8v-40h40a8 8 0 0 0 8-8V40a8 8 0 0 0-8-8m-56 176H48V96h112Zm48-48h-32V88a8 8 0 0 0-8-8H96V48h112Z'/%3E%3C/svg%3E");-webkit-mask:var(--un-icon) no-repeat;mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;display:inline-block;flex-shrink:0;vertical-align:-0.125em;width:1em;height:1em;}
.i-ph-floppy-disk-back-bold{--un-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' display='inline-block' flex-shrink='0' vertical-align='-0.125em' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M208 28H83.31a19.86 19.86 0 0 0-14.14 5.86L33.86 69.17A19.86 19.86 0 0 0 28 83.31V208a20 20 0 0 0 20 20h160a20 20 0 0 0 20-20V48a20 20 0 0 0-20-20M92 52h72v20H92Zm112 152H52V85l16-16v7a20 20 0 0 0 20 20h80a20 20 0 0 0 20-20V52h16Zm-76-96a42 42 0 1 0 42 42a42 42 0 0 0-42-42m0 60a18 18 0 1 1 18-18a18 18 0 0 1-18 18'/%3E%3C/svg%3E");-webkit-mask:var(--un-icon) no-repeat;mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;display:inline-block;flex-shrink:0;vertical-align:-0.125em;width:1em;height:1em;}
.i-ph-folder-bold{--un-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' display='inline-block' flex-shrink='0' vertical-align='-0.125em' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M216 68h-82.61l-26-29.29a20 20 0 0 0-15-6.71H40a20 20 0 0 0-20 20v148.62A19.41 19.41 0 0 0 39.38 220h177.51A19.13 19.13 0 0 0 236 200.89V88a20 20 0 0 0-20-20M44 56h46.61l10.67 12H44Zm168 140H44V92h168Z'/%3E%3C/svg%3E");-webkit-mask:var(--un-icon) no-repeat;mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;display:inline-block;flex-shrink:0;vertical-align:-0.125em;width:1em;height:1em;}
+.i-ph-list-plus{--un-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' display='inline-block' flex-shrink='0' vertical-align='-0.125em' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M32 64a8 8 0 0 1 8-8h176a8 8 0 0 1 0 16H40a8 8 0 0 1-8-8m8 72h176a8 8 0 0 0 0-16H40a8 8 0 0 0 0 16m104 48H40a8 8 0 0 0 0 16h104a8 8 0 0 0 0-16m88 0h-16v-16a8 8 0 0 0-16 0v16h-16a8 8 0 0 0 0 16h16v16a8 8 0 0 0 16 0v-16h16a8 8 0 0 0 0-16'/%3E%3C/svg%3E");-webkit-mask:var(--un-icon) no-repeat;mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;display:inline-block;flex-shrink:0;vertical-align:-0.125em;width:1em;height:1em;}
.i-ph-magnifying-glass-bold{--un-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' display='inline-block' flex-shrink='0' vertical-align='-0.125em' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M232.49 215.51L185 168a92.12 92.12 0 1 0-17 17l47.53 47.54a12 12 0 0 0 17-17ZM44 112a68 68 0 1 1 68 68a68.07 68.07 0 0 1-68-68'/%3E%3C/svg%3E");-webkit-mask:var(--un-icon) no-repeat;mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;display:inline-block;flex-shrink:0;vertical-align:-0.125em;width:1em;height:1em;}
.i-ph-pencil-simple-line-bold{--un-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' display='inline-block' flex-shrink='0' vertical-align='-0.125em' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m230.15 70.54l-44.69-44.68a20 20 0 0 0-28.28 0L33.86 149.17A19.86 19.86 0 0 0 28 163.31V208a20 20 0 0 0 20 20h168a12 12 0 0 0 0-24h-91L230.15 98.83a20 20 0 0 0 0-28.29M91 204H52v-39l84-84l39 39Zm101-101l-39-39l18.34-18.34l39 39Z'/%3E%3C/svg%3E");-webkit-mask:var(--un-icon) no-repeat;mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;display:inline-block;flex-shrink:0;vertical-align:-0.125em;width:1em;height:1em;}
.i-ph-plus-bold{--un-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 256 256' display='inline-block' flex-shrink='0' vertical-align='-0.125em' width='1em' height='1em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M228 128a12 12 0 0 1-12 12h-76v76a12 12 0 0 1-24 0v-76H40a12 12 0 0 1 0-24h76V40a12 12 0 0 1 24 0v76h76a12 12 0 0 1 12 12'/%3E%3C/svg%3E");-webkit-mask:var(--un-icon) no-repeat;mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;display:inline-block;flex-shrink:0;vertical-align:-0.125em;width:1em;height:1em;}