Skip to content
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
04ddf2d
Initial mashup of mentioned feature. Still need to resolve some quirk…
DustyShoe Dec 24, 2025
c7a163f
Merge branch 'invoke-ai:main' into Feature/Add-Text-tool-to-canvas
DustyShoe Dec 24, 2025
c74fc01
Clean text tool integration
DustyShoe Dec 25, 2025
94e5bb4
Merge branch 'Feature/Add-Text-tool-to-canvas' of https://github.com/…
DustyShoe Dec 25, 2025
8ebcfc3
Merge branch 'invoke-ai:main' into Feature/Add-Text-tool-to-canvas
DustyShoe Dec 25, 2025
16bc7e1
Merge branch 'invoke-ai:main' into Feature/Add-Text-tool-to-canvas
DustyShoe Dec 30, 2025
ff28779
Fixed text tool opions bar jumping and added more fonts
DustyShoe Dec 30, 2025
920297e
Touch up for cursor styling
DustyShoe Dec 30, 2025
c14b527
Minor addition to doc file
DustyShoe Dec 30, 2025
8757edd
Appeasing frontend checks
DustyShoe Dec 30, 2025
c79ecd9
Prettier fix
DustyShoe Dec 30, 2025
37c8533
knip fixes
DustyShoe Dec 30, 2025
17e83a6
Added safe zones to font selection and color picker to be clickable w…
DustyShoe Dec 30, 2025
22f147a
Removed color probing on cursor and added dynamic font display for fa…
DustyShoe Dec 30, 2025
c92e6ed
Finally fixed the text shifting on commit
DustyShoe Dec 30, 2025
5742f33
Cursor now represent actual input field size. Tidy up options UI
DustyShoe Dec 30, 2025
db09e2d
Some strikethrough and underline line tweaks
DustyShoe Dec 30, 2025
aea4811
Replaced the focus retry loop with a callback‑ref based approach in i…
DustyShoe Dec 30, 2025
0920a19
Merge branch 'main' into Feature/Add-Text-tool-to-canvas
DustyShoe Dec 30, 2025
fd0e9d1
Added missing localistaion string
DustyShoe Dec 31, 2025
aee1f0d
Merge branch 'main' into Feature/Add-Text-tool-to-canvas
DustyShoe Jan 7, 2026
fd9fd00
Merge branch 'invoke-ai:main' into Feature/Add-Text-tool-to-canvas
DustyShoe Jan 17, 2026
6b87de4
Merge branch 'main' into Feature/Add-Text-tool-to-canvas
DustyShoe Jan 21, 2026
09040e4
Merge branch 'main' into Feature/Add-Text-tool-to-canvas
DustyShoe Jan 27, 2026
9cbfcd9
Merge branch 'main' into Feature/Add-Text-tool-to-canvas
lstein Jan 30, 2026
72f8613
Moved canvas-text-tool.md to docs/contributing/frontend
DustyShoe Jan 30, 2026
e2f2820
ui: Improve functionality of the text toolbar
blessedcoolant Jan 30, 2026
e8c4b10
Added autoselect text in font size on click allowing immediate imput
DustyShoe Jan 31, 2026
957d3ed
Improvement: Added uncommited layer state with CTRL-move and options …
DustyShoe Jan 31, 2026
982eb34
Added rotation handle to rotate uncommiitted text layer.
DustyShoe Feb 1, 2026
ae0478c
Fix: Redirect user facing labels to use localization file + Add tool …
DustyShoe Feb 1, 2026
f9c336f
Fixed box padding. Disable tool swich when text input is active, adde…
DustyShoe Feb 1, 2026
5f7b6bb
Updated Text tool description
DustyShoe Feb 1, 2026
5e57b61
Updated Text tool description
DustyShoe Feb 1, 2026
389a093
Merge branch 'Feature/Add-Text-tool-to-canvas' of https://github.com/…
DustyShoe Feb 1, 2026
ef69847
Typo
DustyShoe Feb 1, 2026
a52375c
Add draggable text-box border with improved cursor feedback and large…
DustyShoe Feb 2, 2026
9956786
Merge branch 'main' into Feature/Add-Text-tool-to-canvas
DustyShoe Feb 3, 2026
062414b
Lint
DustyShoe Feb 3, 2026
3ef54fa
Merge branch 'invoke-ai:main' into Feature/Add-Text-tool-to-canvas
DustyShoe Feb 9, 2026
ad170ba
Fix(bug): text commit to link uploaded image assets instead of embedd…
DustyShoe Feb 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/development/canvas-text-tool.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Canvas Text Tool

## Overview

The canvas text workflow is split between a Konva module that owns tool state and a React overlay that handles text entry.

- `invokeai/frontend/web/src/features/controlLayers/konva/CanvasTool/CanvasTextToolModule.ts`
- Owns the tool, cursor preview, and text session state (including the cursor "T" marker).
- Manages dynamic cursor contrast, starts sessions on pointer down, and commits sessions by rasterizing the active text block into a new raster layer.
- `invokeai/frontend/web/src/features/controlLayers/components/Text/CanvasTextOverlay.tsx`
- Renders the on-canvas editor as a `contentEditable` overlay positioned in canvas space.
- Syncs keyboard input, suppresses app hotkeys, and forwards commits/cancels to the Konva module.
- `invokeai/frontend/web/src/features/controlLayers/components/Text/TextToolOptions.tsx`
- Provides the font dropdown, size slider/input, formatting toggles, and alignment buttons that appear when the Text tool is active.

## Rasterization pipeline

`renderTextToCanvas()` (`invokeai/frontend/web/src/features/controlLayers/text/textRenderer.ts`) converts the editor contents into a transparent canvas. The Text tool module configures the renderer with the active font stack, weight, styling flags, alignment, and the active canvas color. The resulting canvas is encoded to a PNG data URL and stored in a new raster layer (`image` object) with a transparent background.

Layer placement preserves the original click location:

- The session stores the anchor coordinate (where the user clicked) and current alignment.
- `calculateLayerPosition()` calculates the top-left position for the raster layer after applying the configured padding and alignment offsets.
- New layers are inserted directly above the currently-selected raster layer (when present) and selected automatically.

## Font stacks

Font definitions live in `invokeai/frontend/web/src/features/controlLayers/text/textConstants.ts` as ten deterministic stacks (sans, serif, mono, rounded, script, humanist, slab serif, display, narrow, UI serif). Each stack lists system-safe fallbacks so the editor can choose the first available font per platform.

To add or adjust fonts:

1. Update `TEXT_FONT_STACKS` with the new `id`, `label`, and CSS `font-family` stack.
2. If you add a new stack, extend the `TEXT_FONT_IDS` tuple and update the `canvasTextSlice` schema default (`TEXT_DEFAULT_FONT_ID`).
3. Provide translation strings for any new labels in `public/locales/*`.
4. The editor and renderer will automatically pick up the new stack via `getFontStackById()`.
14 changes: 13 additions & 1 deletion invokeai/frontend/web/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2417,7 +2417,19 @@
"bbox": "Bbox",
"move": "Move",
"view": "View",
"colorPicker": "Color Picker"
"colorPicker": "Color Picker",
"text": "Text"
},
"text": {
"font": "Font",
"size": "Size",
"bold": "Bold",
"italic": "Italic",
"underline": "Underline",
"strikethrough": "Strikethrough",
"alignLeft": "Align Left",
"alignCenter": "Align Center",
"alignRight": "Align Right"
},
"filter": {
"filter": "Filter",
Expand Down
3 changes: 3 additions & 0 deletions invokeai/frontend/web/src/app/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { changeBoardModalSliceConfig } from 'features/changeBoardModal/store/sli
import { canvasSettingsSliceConfig } from 'features/controlLayers/store/canvasSettingsSlice';
import { canvasSliceConfig } from 'features/controlLayers/store/canvasSlice';
import { canvasSessionSliceConfig } from 'features/controlLayers/store/canvasStagingAreaSlice';
import { canvasTextSliceConfig } from 'features/controlLayers/store/canvasTextSlice';
import { lorasSliceConfig } from 'features/controlLayers/store/lorasSlice';
import { paramsSliceConfig } from 'features/controlLayers/store/paramsSlice';
import { refImagesSliceConfig } from 'features/controlLayers/store/refImagesSlice';
Expand Down Expand Up @@ -62,6 +63,7 @@ const log = logger('system');
const SLICE_CONFIGS = {
[canvasSessionSliceConfig.slice.reducerPath]: canvasSessionSliceConfig,
[canvasSettingsSliceConfig.slice.reducerPath]: canvasSettingsSliceConfig,
[canvasTextSliceConfig.slice.reducerPath]: canvasTextSliceConfig,
[canvasSliceConfig.slice.reducerPath]: canvasSliceConfig,
[changeBoardModalSliceConfig.slice.reducerPath]: changeBoardModalSliceConfig,
[dynamicPromptsSliceConfig.slice.reducerPath]: dynamicPromptsSliceConfig,
Expand All @@ -87,6 +89,7 @@ const ALL_REDUCERS = {
[api.reducerPath]: api.reducer,
[canvasSessionSliceConfig.slice.reducerPath]: canvasSessionSliceConfig.slice.reducer,
[canvasSettingsSliceConfig.slice.reducerPath]: canvasSettingsSliceConfig.slice.reducer,
[canvasTextSliceConfig.slice.reducerPath]: canvasTextSliceConfig.slice.reducer,
// Undoable!
[canvasSliceConfig.slice.reducerPath]: undoable(
canvasSliceConfig.slice.reducer,
Expand Down
Loading