Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 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
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 @@ -2371,7 +2371,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