Skip to content

feat(sdk): add sticky note node and placement tool action (#1232) - #1233

Merged
jesusmpc merged 14 commits into
InditexTech:mainfrom
jmcaamanog:feat/sdk-sticky-note-node
Nov 12, 2026
Merged

jesusmpc merged 14 commits into
InditexTech:mainfrom
jmcaamanog:feat/sdk-sticky-note-node

Conversation

@jmcaamanog

@jmcaamanog jmcaamanog commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Description

Resolves #1232.

Following the architectural guidance from @jesusmpc in InditexTech/weavejs-frontend#200, this PR implements the Sticky Note canvas primitive and its interactive placement tool action directly within the core @inditextech/weave-sdk.

Changes Included

  1. WeaveStickyNoteNode (code/packages/sdk/src/nodes/sticky-note/):

    • Custom polygonal paper sheet rendering in Konva with a realistic 3D folded corner (dog-ear fold flap) on the bottom-right.
    • Ambient drop shadow and semi-transparent adhesive tape strip at the top.
    • Curated pastel color palette: Yellow (#FEF08A), Mint (#A7F3D0), Rose (#FECDD3), Lavender (#DDD6FE), Sky (#BAE6FD), Amber (#FDE68A), Coral (#FFEDD5), and White (#F9FAFB).
    • Configurable typography (font size, family, color, multi-line wrapping) and author badge (@AUTHOR).
    • Static state management (defaultState, addNodeState, updateNodeState) and schema validation with Zod (getSchema).
    • Full unit test suite (7 tests, 100% passing).
  2. WeaveStickyNoteToolAction (code/packages/sdk/src/actions/sticky-note-tool/):

    • Click-to-place (default 200x200 px) or drag-to-size canvas placement.
    • Live bounding box preview on measureContainer.
    • Automatic node creation and selection transition on completion.
    • Full unit test suite (8 tests, 100% passing).
  3. Public API Exports:

    • Exported WeaveStickyNoteNode and WeaveStickyNoteToolAction (along with constants and types) in code/packages/sdk/src/index.common.ts.
  4. Compliance:

    • 100% compliant with REUSE 3.3 specification (SPDX Apache-2.0 headers on all new files).
    • Commit cryptographically signed with SSH and DCO sign-off.

@jmcaamanog
jmcaamanog requested a review from a team as a code owner October 22, 2026 12:42
@signoff-validator

Copy link
Copy Markdown

Contribution License Agreement - Valid 🟢

All commits contain valid signoff. As a confirmation, remember that you are agreeing to our Contribution License Agreement.

@jesusmpc jesusmpc added the skip-release Do not publish a release on merge. The PR does not need a linked issue. label Oct 26, 2026
@jmcaamanog
jmcaamanog force-pushed the feat/sdk-sticky-note-node branch from 2ccefc3 to 72e6aec Compare October 26, 2026 15:43
@jesusmpc

Copy link
Copy Markdown
Contributor

Thanks for the contribution, @jmcaamanog. The visual primitive looks good, and the tool follows the rectangle-tool lifecycle well. Checks are green on my side too: the SDK suite passes, and eslint, prettier, tsc and reuse lint are clean. Before merging I'd like to align the sticky note with how rectangle, ellipse and polygon handle text and resizing.

1. Use the shape-label engine for the text (main request)

Rectangle/ellipse/polygon don't render text themselves. They share the shape-label engine (#1226). The sticky note currently has text/fontSize/fontFamily/fontColor props and no way to edit text on the canvas, because there is no dblClick and no editor. Please move it onto the same engine:

  • Add label?: Partial<WeaveShapeLabelConfig> to WeaveStickyNoteProperties, and use createShapeLabelEngine(this.instance, this.config.label), so both legacy and native work.
  • Replace the ad-hoc text props with label* props (labelText, labelFontSize, labelFill, ..., labelBlocks). Keep color and author as sticky-specific. Use spreadLabelProps, applyLabelBlocksState and getShapeLabelSchemaFields in addNodeState, updateNodeState and getSchema.
  • Use renderLabel and updateLabel. The text bounds should exclude the padding, the dog-ear and the author margin.
  • Add dblClick plus the getTransformerProperties edit trigger, both gated on selecting and selected, calling triggerEditMode with commitShapeLabel. Pass a grow callback, and guard updateNode with a _transforming flag so growth doesn't write to Yjs mid-resize.
  • Add computeStickyNoteLabelMinSize next to the other compute*LabelMinSize helpers, and set getNodeMinSize. Include the fold and author margin. This replaces the hard-coded MIN_STICKY_NOTE_SIZE = 120, and ellipsis: true can go too, since the height auto-grows.

Doing this now avoids a breaking prop rename later (text to labelText).

2. Live resize

There is no transform handler, so the group is only scaled while dragging. The fold, text and shadow distort, then snap to the right layout on transformend. Rectangle does scaleReset then onUpdate on every transform event. Please do the same.

3. Tool

  • The preview rect in handleMovement isn't clamped to MIN_STICKY_NOTE_SIZE, but the committed note is, so the note jumps on release. A tiny drag also produces 120 rather than the 200 default. Clamp the preview, or fall back to the default size under a threshold.
  • Minor: the first pointermove handler only sets the cursor and is redundant with the second.

4. Smaller points

  • Only main-sheet is listening, so the dog-ear triangle can't be clicked to select.
  • onRender and onUpdate duplicate the layout math (padding, fold, author margin). A shared layout() helper would prevent drift.
  • // @ts-ignore on allowedAnchors: please use a typed approach.
  • config.transform is spread over all transformer properties, but its type only allows enabledAnchors.
  • The author is uppercased and "@"-prefixed in the render. Consider storing it as-is and leaving presentation to the consumer.
  • Seven shapes per note with two shadowBlurs can get heavy on large boards. The shadowRect has both a translucent fill and a shadow, which looks unintended. Consider simplifying.

5. Tests

Nothing currently covers transform, the minimum size, dblClick/label editing, or drag sizing (including Shift/Alt and cancel). Please add tests for the edit flow, labelText/labelBlocks commit, live resize with no leftover scale, the min-size clamp, and the tap versus drag sizes, ideally for both engines.

6. Housekeeping

  • The branch is behind main (it predates 5.7.1), so please rebase.
  • New nodes and actions have docs pages (main:build/nodes|actions/* and sdk:api-reference/*). Either add them here or open a follow-up issue.

Happy to help with any of this, and the rectangle implementation is the reference to copy.

…h#1232)

- Add WeaveStickyNoteNode with 3D dog-ear folded corner and pastel palette
- Integrate shape-label engine (InditexTech#1226) for inline editing and label* props
- Implement live resize with scaleReset and onUpdate to avoid distortions
- Add WeaveStickyNoteToolAction with click-to-place and drag-to-size
- Export node, action, and computeStickyNoteLabelMinSize in index.common.ts
- Expand unit test suites to 41 automated tests (100% passing)
- Update code/CHANGELOG.md under [Unreleased] referencing InditexTech#1232
- Rebase cleanly on origin/main (5.7.1) and ensure REUSE 3.3 compliance

Signed-off-by: jmcaamanog <jose.caamano@live.com>
@jmcaamanog
jmcaamanog force-pushed the feat/sdk-sticky-note-node branch from 72e6aec to f691868 Compare October 27, 2026 20:05
@jmcaamanog

Copy link
Copy Markdown
Contributor Author

Hi @jesusmpc, thanks a lot for the detailed review and guidance!

We have fully refactored the sticky note implementation following the rectangle reference and addressed all 6 points:

  1. Shape-Label Engine (Shape label: use the native-canvas text engine and editor session #1226):

    • Added label?: Partial<WeaveShapeLabelConfig> to WeaveStickyNoteProperties and initialized createShapeLabelEngine(this.instance, this.config.label).
    • Migrated ad-hoc text attributes to standard label* props (labelText, labelFontSize, labelFill, ..., labelBlocks), preserving color and author.
    • Integrated spreadLabelProps, applyLabelBlocksState, and getShapeLabelSchemaFields into addNodeState, updateNodeState, and getSchema().
    • Bounded label rendering via renderLabel / updateLabel, discounting padding, the bottom-right dog-ear fold, and the author margin.
    • Wired canvas inline editing via dblClick and transformer trigger with shapeLabelEditor.triggerEditMode, commitShapeLabel, dynamic vertical growth callback, and _transforming guard to prevent mid-resize Yjs state pollution.
    • Added computeStickyNoteLabelMinSize in nodes/shared/shape-label.utils.ts (accounting for fold flap and author margin) and hooked it into getNodeMinSize. Removed ellipsis: true since height dynamically auto-grows.
  2. Live Resize:

    • Added transformstart, transform (with scaleReset + this.onUpdate()), and transformend handlers so the 3D folded corner, tape, and labels maintain exact geometry without optical distortion during drag transforms.
  3. Tool (WeaveStickyNoteToolAction):

    • Removed the redundant pointermove handler.
    • Added a DRAG_THRESHOLD = 20 threshold to reliably differentiate quick clicks/taps (defaulting to 200×200 px) from drag-to-size gestures.
    • Clamped the drag preview rectangle in handleMovement to MIN_STICKY_NOTE_SIZE = 120 (supporting Shift 1:1 aspect ratio constraint) to eliminate jumps on pointer release.
  4. Architectural & Performance Polish:

    • Enabled pointer interaction on the dog-ear fold flap (listening(true)) so clicks on the corner select the node.
    • Extracted unified getLayout(width, height) helper shared between onRender and onUpdate.
    • Replaced // @ts-ignore with strictly typed allowedAnchors and clean config.transform handling.
    • Preserved author as-is without forcing uppercase or prepending @.
    • Simplified Konva rendering by applying a single ambient drop shadow directly on mainSheet, removing the redundant shadowRect for high-density canvas performance.
  5. Extended Unit Test Suites (41 tests, 100% green):

    • Added comprehensive tests covering live resize (transform resets scale to 1), dblClick edit mode invocation, rich text labelBlocks commit, and getNodeMinSize dynamic growth.
    • Added placement tool action tests for tap default sizing (200×200), drag sizing with 120×120 clamp, Shift 1:1 aspect ratio constraint, and Escape/pointerup cancellation.
    • Added unit tests for computeStickyNoteLabelMinSize in shape-label.utils.test.ts.
  6. Housekeeping & Rebase:

    • Rebased cleanly on latest origin/main (5.7.1).
    • Clean verification across all checks: types:check (0 errors), lint (0 errors), prettier (100% compliant), and full SDK build.
    • Commit cryptographically signed with SSH and DCO sign-off (Signed-off-by).

@jesusmpc jesusmpc added skip-release Do not publish a release on merge. The PR does not need a linked issue. and removed skip-release Do not publish a release on merge. The PR does not need a linked issue. labels Nov 11, 2026
Default sticky note text to top-left aligned Inter 16px with 16px padding
instead of the generic centered Arial 14px shape-label defaults, using the
existing DEFAULT_STICKY_NOTE_* constants. Export STICKY_NOTE_LABEL_DEFAULTS,
STICKY_NOTE_AUTHOR_MARGIN and DEFAULT_STICKY_NOTE_PADDING and reuse them in
computeStickyNoteLabelMinSize so the minimum size matches the layout.

Refs InditexTech#1232

Signed-off-by: Jesus Manuel Piñeiro Cid <jesusmpc@inditex.com>
@jesusmpc

Copy link
Copy Markdown
Contributor

Hi @jmcaamanog, while testing this PR end to end in the showcase (frontend/backend), we found a few things about the default look, so we pushed a small commit to the branch (04daf01, maintainer edits). Thanks again for the work here, and please shout if you would rather handle any of it differently.

What we changed

  • Sticky-specific label defaults. New STICKY_NOTE_LABEL_DEFAULTS in nodes/sticky-note/constants.ts: top-left aligned text (labelAlign: 'left', labelVerticalAlign: 'top'), Inter 16px, color #1F2937, line height 1.35 and 16px padding (labelPaddingX/Y). Before, notes inherited the generic shape-label defaults (centered Arial 14px, 8px padding), and the DEFAULT_STICKY_NOTE_FONT_* constants were declared but unused. They are now used by defaultState, the tool's initProps and the layout fallback.
  • Why top/left. Because the text area excludes the dog-ear fold and the author row, "middle" vertical alignment put the text 14 to 25px above the visual center of the note. Top-left matches how sticky notes are normally used.
  • Shared layout constants. Exported STICKY_NOTE_AUTHOR_MARGIN (22) and DEFAULT_STICKY_NOTE_PADDING (16). computeStickyNoteLabelMinSize now uses them plus DEFAULT_DOG_EAR_SIZE instead of the hard-coded 28/22, so the min size cannot drift from the layout.
  • Tests. Updated the default-state and initProps tests to cover the new defaults. Sticky note, tool and shared label suites pass (140 tests).

Backwards compatibility. Padding and label props are stored per note, so notes created with the previous defaults keep their look. Only newly created notes get the new defaults.

Still open from the earlier review (not touched in this commit): the getTransformerProperties edit trigger, the duplicated drag-threshold logic in the tool (handleMovement vs handleSettingSize), the unused DEFAULT_STICKY_NOTE_TEXT constant, and docs pages.

We are testing this in InditexTech/weavejs-frontend#226 and InditexTech/weavejs-backend#164 (sticky note tool with color swatches and padding controls). We will report anything else we find there.

With labelVerticalAlign 'middle' the text box only excluded the dog-ear fold
and author row at the bottom, so the text sat above the visual center of the
sheet. Reserve the same space on top for middle alignment so the text is
centered on the sheet while still never reaching the fold. Top and bottom
alignment are unchanged. The grow callbacks and computeStickyNoteLabelMinSize
use the same reserve.

Refs InditexTech#1232

Signed-off-by: Jesus Manuel Piñeiro Cid <jesusmpc@inditex.com>
@jesusmpc

Copy link
Copy Markdown
Contributor

Follow-up commit 01f2116 (pushed to the branch with maintainer edits): with labelVerticalAlign: 'middle' the text box only excluded the dog-ear fold and author row at the bottom, so the text sat above the visual center of the sheet. Middle alignment now reserves the same space on top (getStickyNoteVerticalReserve in nodes/sticky-note/constants.ts), so the text is centered on the sheet and still never reaches the fold. Top and bottom alignment are unchanged. The edit grow callbacks and computeStickyNoteLabelMinSize use the same reserve. Added tests for the helper and for the edit bounds (centered for middle, unchanged for top); the sticky note, tool and shared label suites pass (144 tests).

…InditexTech#1232)

- Extract resolveNoteBounds helper to deduplicate handleMovement/handleSettingSize
- Remove unused DEFAULT_STICKY_NOTE_TEXT constant from sticky-note/constants.ts
- Type WeaveStickyNoteProperties.transform with WeaveNodeTransformerProperties
- Test custom config.transform propagation through getTransformerProperties()
- Add main & SDK API docs pages and nav entries for sticky-note node & tool

Signed-off-by: jmcaamanog <jose.caamano@live.com>
@jmcaamanog

Copy link
Copy Markdown
Contributor Author

Hi @jesusmpc, thanks a lot for integrating and testing this end-to-end in InditexTech/weavejs-frontend#226 and InditexTech/weavejs-backend#164, and for the 04daf014f and 01f2116b5 polish commits! Top-left defaults (STICKY_NOTE_LABEL_DEFAULTS), shared layout constants, and symmetric middle vertical centering look much cleaner and prevent drift.

We rebased our follow-up commit on top of 01f2116b5 and wrapped up all four remaining items:

  1. Deduplicated drag threshold logic (WeaveStickyNoteToolAction):

    • Extracted STICKY_NOTE_DRAG_THRESHOLD = 20 and a shared resolveNoteBounds(mousePoint, modifiers) helper used by both handleMovement and handleSettingSize.
  2. Removed unused DEFAULT_STICKY_NOTE_TEXT:

    • Removed the unused constant from nodes/sticky-note/constants.ts.
  3. WeaveStickyNoteProperties.transform & getTransformerProperties check:

    • Updated WeaveStickyNoteProperties.transform in nodes/sticky-note/types.ts to Partial<WeaveNodeTransformerProperties> (instead of restricting it to enabledAnchors) and added a test in sticky-note.test.ts verifying config.transform propagation via getTransformerProperties().
    • Quick clarification on the earlier "getTransformerProperties edit trigger" note: looking at rectangle.ts, ellipse.ts, and polygon.ts, none of them attach an edit trigger inside getTransformerProperties — inline editing is triggered exclusively from dblClick (gated on isSelecting() + isNodeSelected(), which sticky-note.ts already implements). Please let me know if you also want an extra attribute/hook for the showcase!
  4. Documentation pages (Antora):

    • Added main:build/nodes/sticky-note.adoc, main:build/actions/sticky-note-tool.adoc, sdk:api-reference/nodes/sticky-note.adoc (including STICKY_NOTE_LABEL_DEFAULTS and getStickyNoteVerticalReserve), and sdk:api-reference/actions/sticky-note-tool.adoc, and wired them into the module nav.adoc and index.adoc pages.

Verification (run locally after all edits):

  • npm run types:check (@inditextech/weave-sdk): passed with 0 errors.
  • npm run lint (monorepo): passed across all 10 packages.
  • npx vitest run src/nodes/shared src/nodes/sticky-note src/actions/sticky-note-tool: 144 passed (2 skipped in shape-label-server.test.ts).
  • prettier --check --end-of-line auto and reuse lint: 1835/1835 compliant.

Middle vertical alignment now centers the text within the padding only,
ignoring the dog-ear fold and author row, so it is visually centered on the
sheet. Top and bottom alignment keep the text clear of the fold and author.

Refs InditexTech#1232

Signed-off-by: Jesus Manuel Piñeiro Cid <jesusmpc@inditex.com>
Refs InditexTech#1232

Signed-off-by: Jesus Manuel Piñeiro Cid <jesusmpc@inditex.com>
Refs InditexTech#1232

Signed-off-by: Jesus Manuel Piñeiro Cid <jesusmpc@inditex.com>
@jesusmpc

Copy link
Copy Markdown
Contributor

Thanks @jmcaamanog for pushing 6c9792b while we were working on the same review points, it overlapped with some of our changes, so we rebased on top of it and kept your versions (shared tool bounds, types, docs pages). What we pushed on top (c6217be):

  • Middle alignment (620e524): with labelVerticalAlign: 'middle' the text is now centered on the sheet within the padding only, ignoring the fold and the author row (getStickyNoteVerticalReserve returns no reserve for middle). top/bottom keep the text clear of the fold and author. A long middle-aligned text can run over the fold or the author line, that is the trade-off. Tests cover the helper and the edit bounds.
  • Docs (3d78269): updated the SDK node page for the middle alignment behaviour.
  • Ledger (c6217be): registered the four sticky note pages in the AGENTS.md documentation state table.

Review points status: the transformer edit trigger turned out to be a non-issue (rectangle, ellipse and polygon only use dblClick here, same as the sticky note); the preview/commit pointer containers match rectangle-tool; the updateNode in the grow callback follows the same _transforming guard as rectangle; dead constants removed; docs added. Checks: tsc, eslint and the sticky note/tool/shared suites (144 tests) pass, and the docs site builds without warnings.

Middle vertical alignment only reserves the author row below the text, so
the text is centered in the visible free space (and on the sheet when there
is no author). The dog-ear fold is a corner triangle that left-aligned text
rarely reaches, so it is not reserved. Top and bottom alignment still keep
the text clear of the fold and the author row.

Refs InditexTech#1232

Signed-off-by: Jesus Manuel Piñeiro Cid <jesusmpc@inditex.com>
@jesusmpc

Copy link
Copy Markdown
Contributor

Follow-up (863eed2): after trying the variants in the showcase, labelVerticalAlign: 'middle' now reserves only the author row below the text (getStickyNoteVerticalReserve), so the text is centered in the visible free space above the author line, and on the sheet when there is no author. The dog-ear fold is not reserved for middle (it is a corner triangle that left-aligned text rarely reaches); top/bottom still keep the text clear of both. Docs and tests updated (145 tests pass). Also removed the CHANGELOG item from the PR description, the PR itself never touched CHANGELOG.md (the release job generates the entry from #1232).

A filled, stroked and shadowed shape is drawn through an intermediate buffer
canvas by Konva. On the skia server backend this left the sheet blank (white,
no shadow) in server exports of larger notes, while the text and fold still
rendered. Disable perfectDrawEnabled and shadowForStrokeEnabled on the sheet:
the shadow of the 1px stroke is imperceptible and it also avoids extra
offscreen canvases on large boards.

Refs InditexTech#1232

Signed-off-by: Jesus Manuel Piñeiro Cid <jesusmpc@inditex.com>
@jesusmpc

Copy link
Copy Markdown
Contributor

Found while testing server-side export (backend renders rooms with the skia canvas backend): larger sticky notes exported with a blank sheet (white, no color, no shadow) while the text, author and fold still rendered. Cause: the main sheet is filled + stroked + shadowed, which makes Konva draw it through an intermediate buffer canvas, and on the skia backend that left the sheet empty (a 200x214 note rendered fine, a 322x436 one did not; disabling the shadow or the stroke fixed it). Fixed in b95ad0b by setting perfectDrawEnabled: false and shadowForStrokeEnabled: false on the sheet: the shadow of the 1px stroke is imperceptible, and it also avoids extra offscreen canvases on large boards (the perf point from the first review). Added a test for the two attributes.

Side note for server rendering: apps that render rooms on the server need label: { engine: 'native' } on the label nodes (the sticky note included), otherwise the exported text loses its rich formatting (legacy only draws labelText). We fixed that in the showcase backend (rectangle, ellipse, polygon and sticky note).

@jesusmpc jesusmpc added release-type/minor Publish the next minor release. and removed skip-release Do not publish a release on merge. The PR does not need a linked issue. labels Nov 12, 2026
@jesusmpc jesusmpc self-assigned this Nov 12, 2026
@jesusmpc jesusmpc added the release-preview Produce a prerelease or preview release. label Nov 12, 2026
Signed-off-by: Jesus Manuel Piñeiro Cid <jesusmpc@inditex.com>
@jesusmpc
jesusmpc merged commit 52e9d7b into InditexTech:main Nov 12, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-preview Produce a prerelease or preview release. release-type/minor Publish the next minor release.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add sticky note canvas primitive and interactive placement tool

3 participants