diff --git a/frontend/src/components/canvas/SelectionLayer.vue b/frontend/src/components/canvas/SelectionLayer.vue
index 6aa52160..3caab849 100644
--- a/frontend/src/components/canvas/SelectionLayer.vue
+++ b/frontend/src/components/canvas/SelectionLayer.vue
@@ -15,7 +15,10 @@ import { isTextElement, selectionOutline, handleCenter, NEUTRAL_SELECT } from '@
import { cornerRadiusOf, supportsCornerRounding, maxCornerRadius } from '@/diagram/shapeGeometry.js'
import { arrowProportions } from '@/diagram/blockArrow.js'
-const HANDLE = 12
+// Smaller than the shape's own outline stroke would suggest is legible — the
+// Slides-style discoverable-but-quiet affordance #550 asks for, not a control
+// that reads as bigger than the box it resizes.
+const HANDLE = 8
const ROTATION_ARM = 28
const HANDLES = [
'top-left', 'top', 'top-right', 'right',
@@ -316,7 +319,7 @@ function startRotate(event) {
:y="handlePosition(name).y - handleSize / 2"
:width="handleSize"
:height="handleSize"
- :rx="2.5 / zoom"
+ :rx="1.5 / zoom"
fill="#FFFFFF"
:stroke="handleColor"
:stroke-width="strokeWidth"
diff --git a/frontend/src/components/canvas/ShapeView.vue b/frontend/src/components/canvas/ShapeView.vue
index e97da4bd..2869bef7 100644
--- a/frontend/src/components/canvas/ShapeView.vue
+++ b/frontend/src/components/canvas/ShapeView.vue
@@ -203,10 +203,11 @@ const labelFill = computed(() => {
return textStyle.value.color
})
-// Shrink-to-fit the rich text when the shape opts in (spec 6.4). Declared last so
-// the inputs getter can reference the computeds above without hitting the TDZ.
+// Shrink-to-fit the rich text, always on (#550 — no opt-in toggle any more).
+// Declared last so the inputs getter can reference the computeds above without
+// hitting the TDZ.
useAutoFitText(richEl, () => ({
- enabled: props.shape.text?.autoFit && !isEditingThis.value,
+ enabled: !isEditingThis.value,
base: props.shape.text?.style?.size || 16,
w: textArea.value?.w,
h: textArea.value?.h,
diff --git a/frontend/src/components/canvas/WhiteboardStickyNote.vue b/frontend/src/components/canvas/WhiteboardStickyNote.vue
index 699adb1c..863e8485 100644
--- a/frontend/src/components/canvas/WhiteboardStickyNote.vue
+++ b/frontend/src/components/canvas/WhiteboardStickyNote.vue
@@ -23,6 +23,7 @@ import { safeHref } from '@/utils/safeUrl.js'
import { stickyRuns, stickyTextStyle } from '@/diagram/whiteboardModel.js'
import { domToRuns, runsToDom } from '@/utils/richTextDom.js'
import { runsToText, trimRuns } from '@/diagram/richText.js'
+import { NEUTRAL_SELECT } from '@/diagram/selectionChrome.js'
import { roughenRect } from '@/diagram/sketch.js'
import { pointsToPath } from '@/diagram/svgPath.js'
import {
@@ -319,14 +320,14 @@ function openLink(event) {
:height="note.h"
rx="4"
:fill="note.color"
- :stroke="selected ? '#006EDB' : 'rgba(0,0,0,0.08)'"
+ :stroke="selected ? NEUTRAL_SELECT : 'rgba(0,0,0,0.08)'"
:stroke-width="selected ? 1.5 : 1"
style="cursor: move; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.12))"
@pointerdown="startMove"
/>
-
+
@@ -352,15 +353,17 @@ function openLink(event) {
-
+
+ palette sweep (#495); the ink is on the shared grid like every other text.
+ The trigger is the "A" every other text object wears (#550): a filled disc
+ here read as one more paper swatch, on a bar whose first six controls ARE
+ paper swatches. The label stays note-specific so it cannot collide with
+ TextGroup's "Text colour" in a name lookup. -->
-
+ A
diff --git a/frontend/src/components/toolbar/groups/TextGroup.vue b/frontend/src/components/toolbar/groups/TextGroup.vue
index 7bbf993c..8ec4fb04 100644
--- a/frontend/src/components/toolbar/groups/TextGroup.vue
+++ b/frontend/src/components/toolbar/groups/TextGroup.vue
@@ -1,13 +1,14 @@