Skip to content

Commit b4146ec

Browse files
committed
feat: phase 6
1 parent da33604 commit b4146ec

11 files changed

Lines changed: 496 additions & 58 deletions

docs/RESTART_PLAN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# DeepNotes — Restart (greenfield) plan — v4
22

33
> **Last updated:** 2026-05-30
4-
> **Status:** Phase 0 foundation complete. Phase 1 spatial checklist complete. **Phase 2 backend parity verified.** **Phase 3 collab wire parity complete.** **Phase 4 SPA routing + lint complete.** **Phase 5 spatial canvas MVP core interactions complete (create, move, resize, delete notes; Shift+click arrows; mouse + touch pan/zoom).** Remaining: Phase 1 checklist sign-off.
4+
> **Status:** Phase 0 foundation complete. Phase 1 spatial checklist complete. **Phase 2 backend parity verified.** **Phase 3 collab wire parity complete.** **Phase 4 SPA routing + lint complete.** **Phase 5 spatial canvas MVP core interactions complete (create, move, resize, delete notes; Shift+click arrows; mouse + touch pan/zoom).** **Phase 6 in progress: selection system complete (click, multi-select, box select, select all, active element); arrow click-to-select + deletion; note color + z-index rendering; grid background verified.**
55
> **This document replaces all prior restart plan versions.** If a prior statement conflicts with this one, this version wins.
66
> **Analyzed:** 2026-05-30 — additional gaps identified in §0.2–0.4, §3, §4, §6–8. Collab protocol gap and routing/product-model divergence newly documented.
77

new-deepnotes/apps/web/src/features/spatial/DisplayArrow.vue

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ const props = defineProps<{
77
model: ArrowModel;
88
sourceModel?: NoteModel;
99
targetModel?: NoteModel;
10+
selected?: boolean;
11+
}>();
12+
13+
const emit = defineEmits<{
14+
select: [];
15+
toggle: [];
1016
}>();
1117
1218
const line = computed(() => {
@@ -19,13 +25,23 @@ const line = computed(() => {
1925
const y2 = t.pos.value.y;
2026
return { x1, y1, x2, y2 };
2127
});
28+
29+
function onPointerDown(e: PointerEvent) {
30+
if (e.button !== 0) return;
31+
e.stopPropagation();
32+
if (e.ctrlKey || e.metaKey) {
33+
emit("toggle");
34+
} else {
35+
emit("select");
36+
}
37+
}
2238
</script>
2339

2440
<template>
2541
<svg
2642
v-if="line"
2743
data-testid="display-arrow"
28-
class="pointer-events-none absolute top-0 left-0 overflow-visible"
44+
class="absolute top-0 left-0 overflow-visible"
2945
:style="{
3046
width: '1px',
3147
height: '1px',
@@ -37,8 +53,11 @@ const line = computed(() => {
3753
:y1="line.y1 - Math.min(line.y1, line.y2)"
3854
:x2="line.x2 - Math.min(line.x1, line.x2)"
3955
:y2="line.y2 - Math.min(line.y1, line.y2)"
40-
stroke="currentColor"
41-
stroke-width="2"
56+
:stroke="selected ? 'var(--primary)' : 'currentColor'"
57+
:stroke-width="selected ? 3 : 2"
58+
stroke-linecap="round"
59+
class="cursor-pointer"
60+
@pointerdown="onPointerDown"
4261
/>
4362
</svg>
4463
</template>

new-deepnotes/apps/web/src/features/spatial/DisplayNote.vue

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,43 @@ const props = defineProps<{
1010
1111
const emit = defineEmits<{
1212
select: [];
13+
toggle: [];
1314
shiftClick: [];
1415
}>();
1516
17+
const noteColor = computed(() => {
18+
const c = props.model.color.value;
19+
if (c.inherit) return null;
20+
// Simple legacy color mapping to CSS color values
21+
const colorMap: Record<string, string> = {
22+
grey: "#9ca3af",
23+
red: "#ef4444",
24+
green: "#22c55e",
25+
blue: "#3b82f6",
26+
yellow: "#eab308",
27+
purple: "#a855f7",
28+
orange: "#f97316",
29+
pink: "#ec4899",
30+
cyan: "#06b6d4",
31+
black: "#171717",
32+
white: "#f5f5f5",
33+
};
34+
return colorMap[c.value] ?? c.value;
35+
});
36+
1637
const transform = computed(() => {
1738
const { x, y } = props.model.pos.value;
18-
return {
39+
const style: Record<string, string | number> = {
1940
transform: `translate(${x}px, ${y}px)`,
2041
width: props.model.width.value.expanded === "Auto" ? "auto" : `${props.model.width.value.expanded}px`,
42+
zIndex: props.model.zIndex.value,
2143
};
44+
const color = noteColor.value;
45+
if (color) {
46+
style.borderColor = color;
47+
style.backgroundColor = `${color}18`; // 10% opacity tint
48+
}
49+
return style;
2250
});
2351
2452
const frameClasses = computed(() => {
@@ -39,13 +67,22 @@ let noteStartX = 0;
3967
let noteStartY = 0;
4068
4169
function onPointerDown(e: PointerEvent) {
42-
if (!props.model.movable.value || e.button !== 0) return;
70+
if (e.button !== 0) return;
4371
e.stopPropagation();
44-
emit("select");
72+
4573
if (e.shiftKey) {
4674
emit("shiftClick");
4775
return;
4876
}
77+
78+
if (e.ctrlKey || e.metaKey) {
79+
emit("toggle");
80+
} else {
81+
emit("select");
82+
}
83+
84+
if (!props.model.movable.value) return;
85+
4986
dragPointerId = e.pointerId;
5087
startX = e.clientX;
5188
startY = e.clientY;

0 commit comments

Comments
 (0)