-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Feature(UI): Add text tool to canvas #8723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
DustyShoe
wants to merge
39
commits into
invoke-ai:main
Choose a base branch
from
DustyShoe:Feature/Add-Text-tool-to-canvas
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
04ddf2d
Initial mashup of mentioned feature. Still need to resolve some quirk…
DustyShoe c7a163f
Merge branch 'invoke-ai:main' into Feature/Add-Text-tool-to-canvas
DustyShoe c74fc01
Clean text tool integration
DustyShoe 94e5bb4
Merge branch 'Feature/Add-Text-tool-to-canvas' of https://github.com/…
DustyShoe 8ebcfc3
Merge branch 'invoke-ai:main' into Feature/Add-Text-tool-to-canvas
DustyShoe 16bc7e1
Merge branch 'invoke-ai:main' into Feature/Add-Text-tool-to-canvas
DustyShoe ff28779
Fixed text tool opions bar jumping and added more fonts
DustyShoe 920297e
Touch up for cursor styling
DustyShoe c14b527
Minor addition to doc file
DustyShoe 8757edd
Appeasing frontend checks
DustyShoe c79ecd9
Prettier fix
DustyShoe 37c8533
knip fixes
DustyShoe 17e83a6
Added safe zones to font selection and color picker to be clickable w…
DustyShoe 22f147a
Removed color probing on cursor and added dynamic font display for fa…
DustyShoe c92e6ed
Finally fixed the text shifting on commit
DustyShoe 5742f33
Cursor now represent actual input field size. Tidy up options UI
DustyShoe db09e2d
Some strikethrough and underline line tweaks
DustyShoe aea4811
Replaced the focus retry loop with a callback‑ref based approach in i…
DustyShoe 0920a19
Merge branch 'main' into Feature/Add-Text-tool-to-canvas
DustyShoe fd0e9d1
Added missing localistaion string
DustyShoe aee1f0d
Merge branch 'main' into Feature/Add-Text-tool-to-canvas
DustyShoe fd9fd00
Merge branch 'invoke-ai:main' into Feature/Add-Text-tool-to-canvas
DustyShoe 6b87de4
Merge branch 'main' into Feature/Add-Text-tool-to-canvas
DustyShoe 09040e4
Merge branch 'main' into Feature/Add-Text-tool-to-canvas
DustyShoe 9cbfcd9
Merge branch 'main' into Feature/Add-Text-tool-to-canvas
lstein 72f8613
Moved canvas-text-tool.md to docs/contributing/frontend
DustyShoe e2f2820
ui: Improve functionality of the text toolbar
blessedcoolant e8c4b10
Added autoselect text in font size on click allowing immediate imput
DustyShoe 957d3ed
Improvement: Added uncommited layer state with CTRL-move and options …
DustyShoe 982eb34
Added rotation handle to rotate uncommiitted text layer.
DustyShoe ae0478c
Fix: Redirect user facing labels to use localization file + Add tool …
DustyShoe f9c336f
Fixed box padding. Disable tool swich when text input is active, adde…
DustyShoe 5f7b6bb
Updated Text tool description
DustyShoe 5e57b61
Updated Text tool description
DustyShoe 389a093
Merge branch 'Feature/Add-Text-tool-to-canvas' of https://github.com/…
DustyShoe ef69847
Typo
DustyShoe a52375c
Add draggable text-box border with improved cursor feedback and large…
DustyShoe 9956786
Merge branch 'main' into Feature/Add-Text-tool-to-canvas
DustyShoe 062414b
Lint
DustyShoe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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()`. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # Text Tool | ||
|
|
||
| ## Font selection | ||
|
|
||
| The Text tool uses a set of predefined font stacks. When you choose a font, the app resolves the first available font on your system from that stack and uses it for both the editor overlay and the rasterized result. This provides consistent styling across platforms while still falling back to safe system fonts if a preferred font is missing. | ||
|
|
||
| ## Size and spacing | ||
|
|
||
| - **Size** controls the font size in pixels. | ||
| - **Spacing** controls the line height multiplier (Dense, Normal, Spacious). This affects the distance between lines while editing the text. | ||
|
|
||
| ## Uncommitted state | ||
|
|
||
| While text is uncommitted, it remains editable on-canvas. Access to other tools is blocked. Switching to other tabs (Generate, Upascaling, Workflows etc.) discards the text. The uncommitted box can be moved and rotated: | ||
|
|
||
| - **Move:** Hold Ctrl (Windows/Linux) or Command (macOS) and drag to move the text box. | ||
| - **Rotate:** Drag the rotation handle above the box. Hold **Shift** while rotating to snap to 15 degree increments. | ||
|
|
||
| The text is committed to a raster layer when you press **Enter**. Press **Esc** to discard the current text session. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
.../web/src/features/controlLayers/components/CanvasAlerts/CanvasAlertsTextSessionActive.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { Alert, AlertIcon, AlertTitle } from '@invoke-ai/ui-library'; | ||
| import { useStore } from '@nanostores/react'; | ||
| import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate'; | ||
| import { memo } from 'react'; | ||
| import { useTranslation } from 'react-i18next'; | ||
|
|
||
| export const CanvasAlertsTextSessionActive = memo(() => { | ||
| const { t } = useTranslation(); | ||
| const canvasManager = useCanvasManager(); | ||
| const session = useStore(canvasManager.tool.tools.text.$session); | ||
|
|
||
| if (!session || session.status === 'committed') { | ||
| return null; | ||
| } | ||
|
|
||
| return ( | ||
| <Alert status="warning" borderRadius="base" fontSize="sm" shadow="md" w="fit-content"> | ||
| <AlertIcon /> | ||
| <AlertTitle>{t('controlLayers.HUD.textSessionActive')}</AlertTitle> | ||
| </Alert> | ||
| ); | ||
| }); | ||
|
|
||
| CanvasAlertsTextSessionActive.displayName = 'CanvasAlertsTextSessionActive'; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.