Coder Social home page Coder Social logo

personalizedrefrigerator / js-draw Goto Github PK

View Code? Open in Web Editor NEW
64.0 4.0 6.0 9.03 MB

Draw pictures using a pen, touchscreen, or mouse! JS-draw is a freehand drawing library for JavaScript and TypeScript.

Home Page: https://personalizedrefrigerator.github.io/js-draw/typedoc/

License: MIT License

JavaScript 0.66% TypeScript 95.91% CSS 0.14% Shell 0.04% HTML 0.09% SCSS 3.17%
drawing freehand-drawing javascript touchscreen typescript drawing-app infinite-zoom svg

js-draw's Introduction

js-draw logo

js-draw

For example usage, see one of the examples or read the documentation.

Features

Very large zoom range

A core feature of js-draw is its large zoom range (from roughly 10⁻¹⁰x to 10¹⁰x).

Demo

infinite-zoom-demo.mp4

Applications using js-draw can adjust this zoom range with custom EditorSettings.

Touchscreen and stylus support

js-draw supports touchscreen pinch zoom and rotate gestures. To simplify editing, screen rotation snaps to multiples of 90 degrees.

Demo

rotatable-canvas.mp4

It's also possible to disable touch drawing. This can be useful when drawing with a stylus and can be done with either PanZoomTool.setMode or, by a user, with the "hand" tool menu:

screenshot: Hand tool menu at the bottom of the screen includes 'lock rotation' and 'touchscreen panning'. 'Touchscreen panning' is highlighted.

User-configurable tools

With the default toolbar, users can change the pen style, color, and more:

screenshot: Pen configuration menu includes pen size, color, type, stabilization, autocorrect,...

It's possible for applications using js-draw to add custom pen types that can also be customized in this way. It's also possible to save the toolbar state and restore it after reloading the app.

More features

js-draw also supports:

  • Partial stroke erasing

    stroke-eraser.mp4

  • Collaborative editing

  • Saving to and loading from a subset of SVG

API

Creating an Editor

With a bundler that supports importing .css files

To create a new Editor and add it as a child of document.body, use the Editor constructor:

import Editor from 'js-draw';
import 'js-draw/styles';

const editor = new Editor(document.body);

The import js-draw/styles step requires a bundler that can import .css files. For example, webpack with css-loader.

With a bundler that doesn't support importing .css files

Import the pre-bundled version of the editor to apply CSS after loading the page.

import Editor from 'js-draw';
import 'js-draw/bundledStyles';

const editor = new Editor(document.body);

js-draw/bundledStyles is a version of the editor's stylesheets pre-processed by Webpack. As such, importing or including it with a <script src="..."></script> tag applies editor-specific CSS to the document.

Without a bundler

If you're not using a bundler, consider using the pre-bundled editor:

<!-- Replace 1.0.0 with the latest version of js-draw -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundle.js"></script>
<script>
    const editor = new jsdraw.Editor(document.body);
    editor.addToolbar();
    editor.getRootElement().style.height = '600px';
</script>

Note: To ensure the CDN-hosted version of js-draw hasn't been tampered with, consider including an integrity="..." attribute. Read more about using SRI with JSDelivr.

Adding a toolbar

To create a toolbar with buttons for the default tools:

const toolbar = editor.addToolbar();

Save and exit buttons can be added with the .addSaveButton and .addExitButton methods:

toolbar.addSaveButton(() => {
    const svgElem = editor.toSVG();
    console.log('The saved SVG:', svgElem.outerHTML);
});

toolbar.addExitButton(() => {
    // Save here?

    // Removes the editor from the document.
    editor.remove();
});

Custom actions can also be added to the toolbar. For example,

toolbar.addActionButton('Custom', () => {
    // When the action button is pressed
});

or alternatively, with an icon,

toolbar.addActionButton({
  label: 'Custom',
  icon: editor.icons.makeSaveIcon(),
}, () => {
    // Do something here
});

Loading from SVG

editor.loadFromSVG(`
    <svg
        viewBox="156 74 200 150"
        width="200" height="150"
    >
        <path d="M156,150Q190,190 209,217L213,215Q193,187 160,148M209,217Q212,218 236,178L232,176Q210,215 213,215M236,178Q240,171 307,95L305,93Q237,168 232,176M307,95Q312,90 329,78L327,74Q309,87 305,93" fill="#07a837"></path>
    </svg>
`);

Note: While js-draw supports only a small subset of the SVG markup language, it tries to preserve unrecognised SVG elements.

For example, although js-draw doesn't support <circle/> elements,

<svg
    viewBox="156 74 200 150"
    width="200" height="150"
>
    <path d="M156,150Q190,190 209,217L213,215Q193,187 160,148M209,217Q212,218 236,178L232,176Q210,215 213,215M236,178Q240,171 307,95L305,93Q237,168 232,176M307,95Q312,90 329,78L327,74Q309,87 305,93" fill="#07a837"></path>
    <circle cx=200 cy=100 r=40 fill='red'/>
</svg>

renders as

screenshot of the image editor, displaying a green checkmark. The circle is invisible

but exports to

<svg viewBox="156 74 200 150" width="200" height="150" version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg"><g><path d="M156,150M156,150Q190,190 209,217L213,215Q193,187 160,148M209,217M209,217Q212,218 236,178L232,176Q210,215 213,215M236,178M236,178Q240,171 307,95L305,93Q237,168 232,176M307,95M307,95Q312,90 329,78L327,74Q309,87 305,93" fill="#07a837"></path></g><circle cx="200" cy="100" r="40" fill="red"></circle></svg>

which does contain the <circle/> element.

Customizing the background

The background color and style can be customized with editor.setBackgroundStyle. For example,

import { Editor, Color4, BackgroundComponentBackgroundType } from 'js-draw';
const editor = new Editor(document.body);

editor.dispatch(editor.setBackgroundStyle({
    color: Color4.orange,
    type: BackgroundComponentBackgroundType.Grid,
}));

Above, we use editor.dispatch because setBackgroundStyle returns a Command, rather than changing the background style directly. js-draw uses Commands to track actions that can be undone and redone.

By default, .dispatch adds Commands to the undo stack. To avoid this, pass false for the second parameter to .dispatch:

const addToHistory = false;
editor.dispatch(editor.setBackgroundStyle({
    color: Color4.orange,
    type: BackgroundComponentBackgroundType.Grid,
}), addToHistory);

Making the background fill the screen

By default, the background has a fixed size and marks the region that will be saved by .toSVG or .toDataURL. It's possible to make the background auto-resize to the content of the image with editor.image.setAutoresizeEnabled(true):

const editor = new Editor(document.body);

const addToHistory = false;
editor.dispatch(editor.image.setAutoresizeEnabled(true), addToHistory);

// Alternatively, using .setBackgroundStyle:
editor.dispatch(editor.setBackgroundStyle({ autoresize: true }), addToHistory);

Saving

To save as an SVG, use editor.toSVG(), which returns an HTMLSVGElement. Alternatively, if working with very large images that need to be saved in the background, consider using editor.toSVGAsync().

It's also possible to render the editor to a PNG or JPEG data URL. This can be done with editor.toDataURL().

The region of the image that will be saved can be changed by calling editor.image.setImportExportRect or

Settings/configuration

Disabling touchpad panning

Touchpad/mousewheel pan gestures can conflict with gestures used to scroll the document. To turn off touchpad pan gestures (and scrolling the editor with the mousewheel),

const editor = new Editor(document.body, {
    wheelEventsEnabled: false,
});

Alternatively, to only enable touchpad panning when the editor has focus,

const editor = new Editor(document.body, {
    wheelEventsEnabled: 'only-if-focused',
});

Localization

If a user's language is available in src/localizations/ (as determined by navigator.languages), that localization will be used.

To override the default language, use getLocalizationTable([ 'custom locale here' ]). For example,

const editor = new Editor(document.body, {
    // Force the Spanish (Español) localizaiton
    localization: getLocalizationTable([ 'es' ]),
});
Creating a custom localization

See src/localization.ts for a list of strings that can be translated.

Many of the default strings in the editor might be overridden like this:

const editor = new Editor(document.body, {
    // Example partial Spanish localization
    localization: {
        // Not all translated strings need to be specified. If a string isn't given,
        // the English (default) localization will be used

        // Strings for the main editor interface
        // (see packages/js-draw/src/localization.ts)
        loading: (percentage: number) => `Cargando: ${percentage}%...`,
        imageEditor: 'Editor de dibujos',

        undoAnnouncement: (commandDescription: string) => `${commandDescription} fue deshecho`,
        redoAnnouncement: (commandDescription: string) => `${commandDescription} fue rehecho`,

        // Strings for the toolbar
        // (see src/toolbar/localization.ts)
        pen: 'Lapiz',
        eraser: 'Borrador',
        select: 'Selecciona',
        thicknessLabel: 'Tamaño: ',
        colorLabel: 'Color',

        ...
    },
});

Setting the minimum and maximum zoom

By default, the editor's minimum and maximum zoom are very large (2·10-10x and 1012x, respectively). These are configurable by the minZoom and maxZoom settings. For example,

const editor = new Editor(document.body, {
    minZoom: 0.5,
    maxZoom: 2,
});

Changing the editor's color theme

The editor's color theme is specified using CSS. Its default theme looks like this:

.imageEditorContainer {
    /* Deafult colors for the editor -- light mode */

    /* Used for unselected buttons and dialog text. */
    --background-color-1: white;
    --foreground-color-1: black;

    /* Used for some menu/toolbar backgrounds. */
    --background-color-2: #f5f5f5;
    --foreground-color-2: #2c303a;

    /* Used for other menu/toolbar backgrounds. */
    --background-color-3: #e5e5e5;
    --foreground-color-3: #1c202a;

    /* Used for selected buttons. */
    --selection-background-color: #cbdaf1;
    --selection-foreground-color: #2c303a;

    /* Used for dialog backgrounds */
    --background-color-transparent: rgba(105, 100, 100, 0.5);

    /* Used for shadows */
    --shadow-color: rgba(0, 0, 0, 0.5);

    /* Color used for some button/input foregrounds */
    --primary-action-foreground-color: #15b;
}

@media (prefers-color-scheme: dark) {
    .imageEditorContainer {
        /* Default colors for the editor -- dark mode */
        --background-color-1: #151515;
        --foreground-color-1: white;

        --background-color-2: #222;
        --foreground-color-2: #efefef;

        --background-color-3: #272627;
        --foreground-color-3: #eee;

        --selection-background-color: #607;
        --selection-foreground-color: white;
        --shadow-color: rgba(250, 250, 250, 0.5);
        --background-color-transparent: rgba(50, 50, 50, 0.5);

        --primary-action-foreground-color: #7ae;
    }
}

To override it, use a more specific CSS selector to set the theme variables. For example,

/* Notice the "body" below -- the selector needs to be more specific than what's in js-draw */
body .imageEditorContainer {
    --background-color-1: green;
    --foreground-color-1: black;

    /* For this theme, use the same secondary and tertiary colors
       (it's okay for them to be the same). */
    --background-color-2: lime;
    --foreground-color-2: black;
    --background-color-3: lime;
    --foreground-color-3: black;

    --background-color-transparent: rgba(255, 240, 200, 0.5);
    --shadow-color: rgba(0, 0, 0, 0.5);

    --selection-background-color: yellow;
    --selection-foreground-color: black;
}

disables the dark theme and creates a theme that primarily uses yellow/green colors.

See also adjustEditorThemeForContrast.

Examples and resources

js-draw's People

Contributors

dependabot[bot] avatar personalizedrefrigerator avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

js-draw's Issues

Dimension automatically set to `100` when the input field is empty.

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when I set the dimension of the drawing. It automatically set to 100 as I clear the input field. This makes the process tedious as I need to make sure the input field to not evaluate to 0, like append a random non-zero number at the end of the dimension before I change the digit in front.

Describe the solution you'd like
It is better to raise some sort of error below the input field when it is evaluated to 0. Such as:

Dimension cannot be 0px. It will default to 100px.

And then only when the input form is closed, it will default to 100px when the user input evaluate to 0.

Keep up the great work! This plugin is very likely to save my academic life! You are my saviour now.

Erase only parts an eraser brush? (images)

How would I go about adding an eraser button that erases only where you move the mouse, like a traditional eraser 'brush'?

Additionally I don't understand yet how js-draw implements events, or how to access the EventDispatcher.
For example, how do I set an event listener "on blur" on every layer so that as soon as a new layer is added/unfocused, all layers are merged into one and the editor image is resized to the size of the resulting layer?

[Translation]: TEST issue

Language

Spanish

pen

Lapiz

eraser

Borrador

select

Selecciona

handTool

Mover

zoom

Zoom

image

Imagen

reformatSelection

Format selection

inputAltText

Alt text:

chooseFile

Choose file:

submit

Submit

cancel

Cancel

resetView

Reiniciar vista

thicknessLabel

Tamaño:

colorLabel

Color:

fontLabel

Fuente:

textSize

Tamaño

resizeImageToSelection

Redimensionar la imagen a lo que está seleccionado

deleteSelection

Borra la selección

duplicateSelection

Duplica la selección

undo

Deshace

redo

Rehace

selectPenType

Forma de dibuja:

pickColorFromScreen

Selecciona un color de la pantalla

clickToPickColorAnnouncement

Haga un clic en la pantalla para seleccionar un color

colorSelectionCanceledAnnouncement

Color selection canceled

selectionToolKeyboardShortcuts

Selection tool: Use arrow keys to move selected items, lowercase/uppercase ‘i’ and ‘o’ to resize.

documentProperties

Fondo

backgroundColor

Color de fondo:

imageWidthOption

Ancho:

imageHeightOption

Alto:

useGridOption

No response

toggleOverflow

Más

touchPanning

Mover la pantalla con un dedo

freehandPen

Dibuja sin restricción de forma

pressureSensitiveFreehandPen

Freehand (pressure sensitive)

arrowPen

Flecha

linePen

Línea

outlinedRectanglePen

Rectángulo con nada más que un borde

filledRectanglePen

Rectángulo sin borde

lockRotation

Bloquea rotación

paste

Paste

dropdownShown

function (toolName) { return "Men\u00FA por ".concat(toolName, " es visible"); }

dropdownHidden

function (toolName) { return "Men\u00FA por ".concat(toolName, " fue ocultado"); }

zoomLevel

function (zoomPercent) { return "Zoom: ".concat(zoomPercent, "%"); }

colorChangedAnnouncement

function (color) { return "Color fue cambiado a ".concat(color); }

imageSize

function (size, units) { return "Tama\u00F1o del imagen: ".concat(size, " ").concat(units); }

errorImageHasZeroSize

Error: Image has zero size

imageLoadError

function (message) { return "Error cargando imagen: ".concat(message); }

penTool

function (penId) { return "Lapiz ".concat(penId); }

selectionTool

Selecciona

selectAllTool

Select all shortcut

eraserTool

Borrador

touchPanTool

Instrumento de mover la pantalla con un dedo

twoFingerPanZoomTool

Panning and zooming

undoRedoTool

Undo/Redo

rightClickDragPanTool

Right-click drag

pipetteTool

Pick color from screen

keyboardPanZoom

Mover la pantalla con el teclado

textTool

Texto

enterTextToInsert

Entra texto

changeTool

Change tool

pasteHandler

Copy paste handler

soundExplorer

Sound-based image exploration

disableAccessibilityExploreTool

Disable sound-based exploration

enableAccessibilityExploreTool

Enable sound-based exploration

findLabel

Find

toNextMatch

Next

closeFindDialog

Close

findDialogShown

Find dialog shown

findDialogHidden

Find dialog hidden

focusedFoundText

function (matchIdx, totalMatches) { return "Viewing match ".concat(matchIdx, " of ").concat(totalMatches); }

anyDevicePanning

Mover la pantalla con todo dispotivo

copied

function (count, description) { return "Copied ".concat(count, " ").concat(description); }

pasted

function (count, description) { return "Pasted ".concat(count, " ").concat(description); }

toolEnabledAnnouncement

function (toolName) { return "".concat(toolName, " enabled"); }

toolDisabledAnnouncement

function (toolName) { return "".concat(toolName, " disabled"); }

updatedViewport

Transformed Viewport

transformedElements

function (elemCount) { return "Transformed ".concat(elemCount, " element").concat(elemCount === 1 ? '' : 's'); }

resizeOutputCommand

function (newSize) { return "Resized image to ".concat(newSize.w, "x").concat(newSize.h); }

addElementAction

function (componentDescription) { return "Added ".concat(componentDescription); }

eraseAction

function (componentDescription, numElems) { return "Erased ".concat(numElems, " ").concat(componentDescription); }

duplicateAction

function (componentDescription, numElems) { return "Duplicated ".concat(numElems, " ").concat(componentDescription); }

unionOf

function (actionDescription, actionCount) { return "Union: ".concat(actionCount, " ").concat(actionDescription); }

inverseOf

function (actionDescription) { return "Inverse of ".concat(actionDescription); }

elements

Elements

erasedNoElements

Erased nothing

duplicatedNoElements

Duplicated nothing

rotatedBy

function (degrees) { return "Rotated by ".concat(Math.abs(degrees), " degrees ").concat(degrees < 0 ? 'clockwise' : 'counter-clockwise'); }

movedLeft

Moved left

movedUp

Moved up

movedDown

Moved down

movedRight

Moved right

zoomedOut

Zoomed out

zoomedIn

Zoomed in

selectedElements

function (count) { return "Selected ".concat(count, " element").concat(count === 1 ? '' : 's'); }

unlabeledImageNode

Unlabeled image

stroke

Stroke

svgObject

SVG Object

emptyBackground

Empty background

gridBackground

Grid background

filledBackgroundWithColor

function (color) { return "Filled background (".concat(color, ")"); }

text

function (text) { return "Text object: ".concat(text); }

imageNode

function (label) { return "Image: ".concat(label); }

restyledElement

function (elementDescription) { return "Restyled ".concat(elementDescription); }

pathNodeCount

function (count) { return "There are ".concat(count, " visible path objects."); }

textNodeCount

function (count) { return "There are ".concat(count, " visible text nodes."); }

imageNodeCount

function (nodeCount) { return "There are ".concat(nodeCount, " visible image nodes."); }

textNode

function (content) { return "Text: ".concat(content); }

rerenderAsText

Redibuja la pantalla al texto

accessibilityInputInstructions

Press "t" to read the contents of the viewport as text. Use the arrow keys to move the viewport, click and drag to draw strokes. Press "w" to zoom in and "s" to zoom out.

loading

function (percentage) { return "Cargando: ".concat(percentage, "%..."); }

imageEditor

Editor de dibujos

doneLoading

El cargado terminó

undoAnnouncement

function (commandDescription) { return "".concat(commandDescription, " fue deshecho"); }

redoAnnouncement

function (commandDescription) { return "".concat(commandDescription, " fue rehecho"); }

Additional information

No response

Update Spanish translation

Language

es

pen

Lapiz

eraser

Borrador

select

Selecciona

handTool

Mover

zoom

No response

image

Imagen

reformatSelection

No response

inputAltText

No response

chooseFile

Seleccionar archivo

dragAndDropHereOrBrowse

No response

submit

No response

cancel

Cancelar

resetView

Reiniciar vista

thicknessLabel

Tamaño

colorLabel

No response

fontLabel

Fuente:

textSize

Tamaño

resizeImageToSelection

Redimensionar la imagen a lo que está seleccionado

deleteSelection

Borra la selección

duplicateSelection

Duplica la selección

exit

Salir

save

Guardar

undo

Deshace

redo

Rehace

selectPenTip

Forma de dibuja

selectShape

Forma

pickColorFromScreen

Selecciona un color de la pantalla

clickToPickColorAnnouncement

Haga un clic en la pantalla para seleccionar un color

colorSelectionCanceledAnnouncement

No response

selectionToolKeyboardShortcuts

No response

documentProperties

Fondo

backgroundColor

Color de fondo:

imageWidthOption

Ancho

imageHeightOption

Alto

useGridOption

No response

enableAutoresizeOption

No response

toggleOverflow

Más

about

No response

inputStabilization

No response

touchPanning

Mover la pantalla con un dedo

roundedTipPen

Lapiz Redondeado

flatTipPen

No response

arrowPen

Flecha

linePen

Línea

outlinedRectanglePen

Rectángulo con nada más que un borde

filledRectanglePen

Rectángulo sin borde

outlinedCirclePen

No response

lockRotation

Bloquea rotación

paste

Pegar

closeSidebar

(toolName) => `Close sidebar for ${toolName}`

dropdownShown

dropdownShown(toolName) { return `Menú por ${toolName} es visible`; }

dropdownHidden

function (toolName) { return `Menú por ${toolName} fue ocultado`; }

zoomLevel

(zoomPercent) => `Zoom: ${zoomPercent}%`

colorChangedAnnouncement

function (color) { return `Color fue cambiado a ${color}`; }

imageSize

(size, units) => `Tamaño del imagen: ${size} ${units}`

errorImageHasZeroSize

No response

imageLoadError

(message) => `Error cargando imagen: ${message}`

penTool

function (penId) { return `Lapiz ${penId}`; }

selectionTool

Selecciona

selectAllTool

No response

eraserTool

Borrador

touchPanTool

Instrumento de mover la pantalla con un dedo

twoFingerPanZoomTool

No response

undoRedoTool

No response

rightClickDragPanTool

No response

pipetteTool

Seleccione un color de la pantalla

keyboardPanZoom

Mover la pantalla con el teclado

textTool

Texto

enterTextToInsert

Entra texto

changeTool

No response

pasteHandler

No response

soundExplorer

No response

disableAccessibilityExploreTool

No response

enableAccessibilityExploreTool

No response

soundExplorerUsageAnnouncement

No response

findLabel

Buscar

toNextMatch

Próxima

closeDialog

Cerrar

findDialogShown

No response

findDialogHidden

No response

focusedFoundText

(matchIdx, totalMatches) => `Viewing match ${matchIdx} of ${totalMatches}`

anyDevicePanning

Mover la pantalla con todo dispotivo

copied

(count, description) => `Copied ${count} ${description}`

pasted

(count, description) => `Pasted ${count} ${description}`

toolEnabledAnnouncement

(toolName) => `${toolName} enabled`

toolDisabledAnnouncement

(toolName) => `${toolName} disabled`

updatedViewport

No response

transformedElements

(elemCount) => `Transformed ${elemCount} element${elemCount === 1 ? '' : 's'}`

resizeOutputCommand

(newSize) => `Resized image to ${newSize.w}x${newSize.h}`

enabledAutoresizeOutputCommand

No response

disabledAutoresizeOutputCommand

No response

addElementAction

(componentDescription) => `Added ${componentDescription}`

eraseAction

(componentDescription, numElems) => `Erased ${numElems} ${componentDescription}`

duplicateAction

(componentDescription, numElems) => `Duplicated ${numElems} ${componentDescription}`

unionOf

(actionDescription, actionCount) => `Union: ${actionCount} ${actionDescription}`

inverseOf

(actionDescription) => `Inverse of ${actionDescription}`

elements

No response

erasedNoElements

No response

duplicatedNoElements

No response

rotatedBy

(degrees) => `Rotated by ${Math.abs(degrees)} degrees ${degrees < 0 ? 'clockwise' : 'counter-clockwise'}`

movedLeft

No response

movedUp

No response

movedDown

No response

movedRight

No response

zoomedOut

No response

zoomedIn

No response

selectedElements

(count) => `Selected ${count} element${count === 1 ? '' : 's'}`

unlabeledImageNode

No response

stroke

No response

svgObject

No response

emptyBackground

No response

gridBackground

No response

filledBackgroundWithColor

(color) => `Filled background (${color})`

text

(text) => `Text object: ${text}`

imageNode

(label) => `Image: ${label}`

restyledElement

(elementDescription) => `Restyled ${elementDescription}`

pathNodeCount

(count) => `There are ${count} visible path objects.`

textNodeCount

(count) => `There are ${count} visible text nodes.`

imageNodeCount

(nodeCount) => `There are ${nodeCount} visible image nodes.`

textNode

(content) => `Text: ${content}`

rerenderAsText

Redibuja la pantalla al texto

accessibilityInputInstructions

No response

loading

(percentage) => `Cargando: ${percentage}%...`

imageEditor

Editor de dibujos

doneLoading

El cargado terminó

undoAnnouncement

(commandDescription) => `${commandDescription} fue deshecho`

redoAnnouncement

(commandDescription) => `${commandDescription} fue rehecho`

Additional information

No response

[Translation]: Update Spanish localization (es)

Language

es

pen

Lapiz

eraser

Borrador

select

Selecciona

handTool

Mover

zoom

No response

image

Imagen

reformatSelection

No response

inputAltText

No response

chooseFile

Seleccione un archivo

dragAndDropHereOrBrowse

No response

submit

No response

cancel

No response

resetView

Reiniciar vista

thicknessLabel

Tamaño

colorLabel

No response

fontLabel

Fuente:

textSize

Tamaño

resizeImageToSelection

Redimensionar la imagen a lo que está seleccionado

deleteSelection

Borra la selección

duplicateSelection

Duplica la selección

exit

Salir

save

Guardar

undo

Deshace

redo

Rehace

selectPenTip

Forma de dibuja

selectShape

Forma

pickColorFromScreen

Selecciona un color de la pantalla

clickToPickColorAnnouncement

Haga un clic en la pantalla para seleccionar un color

colorSelectionCanceledAnnouncement

No response

selectionToolKeyboardShortcuts

No response

documentProperties

Fondo

backgroundColor

Color de fondo:

imageWidthOption

Ancho

imageHeightOption

Alto

useGridOption

No response

enableAutoresizeOption

No response

toggleOverflow

Más

about

No response

inputStabilization

No response

touchPanning

Mover la pantalla con un dedo

roundedTipPen

Lapiz Redondeado

flatTipPen

No response

arrowPen

Flecha

linePen

Línea

outlinedRectanglePen

Rectángulo con nada más que un borde

filledRectanglePen

Rectángulo sin borde

outlinedCirclePen

Círculo delineado

lockRotation

Bloquea rotación

paste

Pegar

closeSidebar

(toolName) => Close sidebar for ${toolName}

dropdownShown

dropdownShown(toolName) { return Menú por ${toolName} es visible; }

dropdownHidden

function (toolName) { return Menú por ${toolName} fue ocultado; }

zoomLevel

(zoomPercent) => Zoom: ${zoomPercent}%

colorChangedAnnouncement

function (color) { return Color fue cambiado a ${color}; }

imageSize

(size, units) => Tamaño del imagen: ${size} ${units}

errorImageHasZeroSize

No response

imageLoadError

(message) => Error cargando imagen: ${message}

penTool

function (penId) { return Lapiz ${penId}; }

selectionTool

Selecciona

selectAllTool

No response

eraserTool

Borrador

touchPanTool

Instrumento de mover la pantalla con un dedo

twoFingerPanZoomTool

No response

undoRedoTool

No response

rightClickDragPanTool

No response

pipetteTool

No response

keyboardPanZoom

Mover la pantalla con el teclado

textTool

Texto

enterTextToInsert

Entra texto

changeTool

No response

pasteHandler

No response

soundExplorer

No response

disableAccessibilityExploreTool

No response

enableAccessibilityExploreTool

No response

soundExplorerUsageAnnouncement

No response

findLabel

Buscar

toNextMatch

Próxima

closeDialog

Cerrar

findDialogShown

No response

findDialogHidden

No response

focusedFoundText

(matchIdx, totalMatches) => Viewing match ${matchIdx} of ${totalMatches}

anyDevicePanning

Mover la pantalla con todo dispotivo

copied

(count, description) => Copied ${count} ${description}

pasted

(count, description) => Pasted ${count} ${description}

toolEnabledAnnouncement

(toolName) => ${toolName} enabled

toolDisabledAnnouncement

(toolName) => ${toolName} disabled

updatedViewport

No response

transformedElements

(elemCount) => Transformed ${elemCount} element${elemCount === 1 ? '' : 's'}

resizeOutputCommand

(newSize) => Resized image to ${newSize.w}x${newSize.h}

enabledAutoresizeOutputCommand

No response

disabledAutoresizeOutputCommand

No response

addElementAction

(componentDescription) => Added ${componentDescription}

eraseAction

(componentDescription, numElems) => Erased ${numElems} ${componentDescription}

duplicateAction

(componentDescription, numElems) => Duplicated ${numElems} ${componentDescription}

unionOf

(actionDescription, actionCount) => Union: ${actionCount} ${actionDescription}

inverseOf

(actionDescription) => Inverse of ${actionDescription}

elements

No response

erasedNoElements

No response

duplicatedNoElements

No response

rotatedBy

(degrees) => Rotated by ${Math.abs(degrees)} degrees ${degrees < 0 ? 'clockwise' : 'counter-clockwise'}

movedLeft

No response

movedUp

No response

movedDown

No response

movedRight

No response

zoomedOut

No response

zoomedIn

No response

selectedElements

(count) => Selected ${count} element${count === 1 ? '' : 's'}

unlabeledImageNode

No response

stroke

No response

svgObject

No response

emptyBackground

No response

gridBackground

No response

filledBackgroundWithColor

(color) => Filled background (${color})

text

(text) => Text object: ${text}

imageNode

(label) => Image: ${label}

restyledElement

(elementDescription) => Restyled ${elementDescription}

pathNodeCount

(count) => There are ${count} visible path objects.

textNodeCount

(count) => There are ${count} visible text nodes.

imageNodeCount

(nodeCount) => There are ${nodeCount} visible image nodes.

textNode

(content) => Text: ${content}

rerenderAsText

Redibuja la pantalla al texto

accessibilityInputInstructions

No response

loading

(percentage) => Cargando: ${percentage}%...

imageEditor

Editor de dibujos

doneLoading

El cargado terminó

undoAnnouncement

(commandDescription) => ${commandDescription} fue deshecho

redoAnnouncement

(commandDescription) => ${commandDescription} fue rehecho

Additional information

This is a partial update of the Spanish translation.

"fixable" line size

I have started using js-draw in my notes app.

Unfortunately, I found out that the adjustable pen size is not properly taken into account,
as it still scales with the zoom level, which makes handwritten notes look really unattractive.

It would be great if you could switch the scaling size on and off with a button in the pen menu, as it is practical in principle.

Bug: Assign the eraser to a pen button

Describe the issue

Following up the discussion on the Forum, here the bug report / feature request

Steps to reproduce the issue

  1. Open a freehand drawing
  2. Use the Pen buttons

Expected behavior

I would like to assign the pen buttons to do something useful, e.g. the eraser function

Observed behavior

Nothing happens when pressing any of the buttons.

Version information

2.11.0

Platform

Joplin Desktop v3.0.9

Logs

input.log

Additional information

Currently using the Lenovo precision pen 2

Better stroke smoothing

Currently, js-draw uses heuristics to fit pen input points to Bézier curves. The current stroke smoothing method leads to handwriting that looks somewhat like this:

Screenshot: "Test" written. The "e" and "s" letters look un-smooth

While the current stroke smoother has the advantage of producing a small number of curves (good because js-draw drawings are uncompressed, Joplin seems not to support SVGZ), it makes handwriting somewhat difficult to read.

Progress

blank page as default template

I would suggest using a blank page as the default template for js draw like all the other programs like onenote:
image
samsung notes:
SmartSelect_20230919_233344_Samsung Notes

especially if you are calling it "drawing"

but either way i can see that that is easy to change. Still... If you need a grid you will turn it on, otherwise a drawing does not need lines...

mouse click for selection does not make much sense

Everything in the toolbar was clear or almost clear for me, except the mouse

I was not sure what it was, clicked on it and through trying understood that it was the selction tool.
The desiign convention for selection is this dotted line and i would suggest we use it.
SmartSelect_20230919_001622_Samsung Notes

Bug: Insert image tool doesn't work in iOS lockdown mode

Describe the issue

Lockdown mode in iOS disables FileReader, which is used by js-draw to convert images to a base64 URL.

Steps to reproduce the issue

  1. Enable lockdown mode
  2. Open the image menu
  3. Choose and try to insert an image

Expected behavior

Should be able to insert images.

Observed behavior

No submit button is visible, an error message is displayed about FileReader being undefined.

Version information

1.13.1

Platform

Safari on iOS 17.1.1

Logs

N/A

Additional information

No response

Bug: Strokes Are Deleting Themselves

Describe the issue

I've encountered a problem when drawing with my pen tablet (Huion Inspiroy H950P). Whenever I draw a stroke, it gets deleted as soon as I try to draw a second stroke. Interestingly, this issue doesn't occur when I use my mouse—everything works perfectly fine then.

Steps to reproduce the issue

Given that I haven't faced any problems with other drawing tools, I suspect the issue isn't with the hardware. Unfortunately, I'm not sure how to reproduce the error consistently. The problem surfaced when I simply entered the homepage and started drawing.

Expected behavior

No response

Observed behavior

No response

Version information

unknown, current version of the web version

Platform

Firefox 126.0.1

Logs

No response

Additional information

js-draw.Mozilla.Firefox.2024-06-07.16-32-15.mp4

Import PDF in A4 format for better export experience

Is your feature request related to a problem? Please describe.
I want to import an A4 PDF file into js-draw. Each page should be automatically scaled to fit perfectly on an A4 page when I export the Markdown file to a PDF (via Joplin?).

Describe the solution you'd like
The end result should be a nice, annotated PDF. Each PDF page should be exactly one page when exported after editing. Assuming that the imported PDF was also A4 should make it easier?

Additional context
As a computer science student, I often have exercise sheets in PDF format with tables to fill in or blank areas for calculations. It would be great to import the PDF file, make the changes and then export it as the same PDF file as before.

Bug: Build Error on Next.js 14 on v1.20.2, works on v1.18.0

Describe the issue

Module not found: Can't resolve './SVGLoader.mjs'
Module not found: Can't resolve './widgets/InsertImageWidget.mjs'

Steps to reproduce the issue

  1. Install [email protected] in Next.js Project
  2. Import in project
  3. Run next build

Expected behavior

Build Successful

Observed behavior

js-draw works on v1.18.0

What I suspect from the changes v1.18.0 to v1.19.0 is SVGLoader.ts is moved to SVGLoader/index.ts.
But in the js-draw/dist/mjs/lib.mjs, SVGLoader.ts import doesn't changed

packages/js-draw/src/SVGLoader.ts → packages/js-draw/src/SVGLoader/index.ts

image

Version information

1.20.2

Platform

No response

Logs

> next build

  ▲ Next.js 14.2.3
  - Experiments (use with caution):
    · instrumentationHook

   Creating an optimized production build ...
Failed to compile.

./node_modules/js-draw/dist/mjs/Editor.mjs:10:1
Module not found: Can't resolve './SVGLoader.mjs'

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./node_modules/js-draw/dist/mjs/lib.mjs
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/process-action/document-sign/Dialog.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/process-action/ProcessActionDocumentSign.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/ApplicantPreview.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/ApplicantPage.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/page.tsx

./node_modules/js-draw/dist/mjs/image/export/editorImageToSVG.mjs:3:1
Module not found: Can't resolve '../../SVGLoader.mjs'

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./node_modules/js-draw/dist/mjs/Editor.mjs
./node_modules/js-draw/dist/mjs/lib.mjs
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/process-action/document-sign/Dialog.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/process-action/ProcessActionDocumentSign.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/ApplicantPreview.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/ApplicantPage.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/page.tsx

./node_modules/js-draw/dist/mjs/lib.mjs:23:1
Module not found: Can't resolve './SVGLoader.mjs'

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/process-action/document-sign/Dialog.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/process-action/ProcessActionDocumentSign.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/ApplicantPreview.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/ApplicantPage.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/page.tsx

./node_modules/js-draw/dist/mjs/rendering/renderers/SVGRenderer.mjs:2:1
Module not found: Can't resolve '../../SVGLoader.mjs'

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./node_modules/js-draw/dist/mjs/rendering/lib.mjs
./node_modules/js-draw/dist/mjs/lib.mjs
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/process-action/document-sign/Dialog.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/process-action/ProcessActionDocumentSign.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/ApplicantPreview.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/ApplicantPage.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/page.tsx

./node_modules/js-draw/dist/mjs/toolbar/AbstractToolbar.mjs:28:1
Module not found: Can't resolve './widgets/InsertImageWidget.mjs'

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./node_modules/js-draw/dist/mjs/lib.mjs
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/process-action/document-sign/Dialog.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/process-action/ProcessActionDocumentSign.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/preview/ApplicantPreview.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/_components/ApplicantPage.tsx
./src/app/admin/(menu)/(shell)/job-schedule/applicant/[id]/page.tsx


> Build failed because of webpack errors

Additional information

No response

Use compressed SVG to reduce file size

Is your feature request related to a problem? Please describe.
The generated SVG file size can be so large in some cases.

Describe the solution you'd like
Use compressed SVG(*.svgz) as the default file format to reduce the generated file size, or add an option to let the users decide the file format.

Additional context
Hi. Thank you for this great project.
The compressed SVG is not widely used because the HTTP server might compress its response, so there is no need to use the compressed one. However, if the SVG file is used locally, I think this file format will save a lot of space.
I don't know if it's difficult to add the support of compressed SVG file. Here are some links about the compressed SVG file. I hope they can help you.
https://en.wikipedia.org/wiki/Scalable_Vector_Graphics#Compression
https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Getting_Started#svg_file_types

Stylus eraser button support

Currently, the eraser button on physical styluses does nothing.

At present, this is because Chromium seems to not report stylus events when the stylus is down (just when the stylus is above the screen), or a bug in how js-draw is registering pointer event listeners.

Even if we can only listen for button events when the pointer is above the screen, pressing the button could be used to, for example, switch tools.

Testing: Translation templates

Language

es

pen

Lapiz

eraser

Borrador

select

Selecciona

handTool

Mover

zoom

No response

image

Imagen

reformatSelection

No response

inputAltText

No response

chooseFile

No response

dragAndDropHereOrBrowse

No response

submit

No response

cancel

No response

resetView

Reiniciar vista

thicknessLabel

Tamaño

colorLabel

No response

fontLabel

Fuente:

textSize

Tamaño

resizeImageToSelection

Redimensionar la imagen a lo que está seleccionado

deleteSelection

Borra la selección

duplicateSelection

Duplica la selección

exit

No response

save

No response

undo

Deshace

redo

Rehace

selectPenTip

Forma de dibuja

selectShape

No response

pickColorFromScreen

Selecciona un color de la pantalla

clickToPickColorAnnouncement

Haga un clic en la pantalla para seleccionar un color

colorSelectionCanceledAnnouncement

No response

selectionToolKeyboardShortcuts

No response

documentProperties

Fondo

backgroundColor

Color de fondo:

imageWidthOption

Ancho

imageHeightOption

Alto

useGridOption

No response

enableAutoresizeOption

No response

toggleOverflow

Más

about

No response

inputStabilization

No response

touchPanning

Mover la pantalla con un dedo

roundedTipPen

Lapiz Redondeado

flatTipPen

No response

arrowPen

Flecha

linePen

Línea

outlinedRectanglePen

Rectángulo con nada más que un borde

filledRectanglePen

Rectángulo sin borde

outlinedCirclePen

No response

lockRotation

Bloquea rotación

paste

No response

closeSidebar

(toolName) => Close sidebar for ${toolName}

dropdownShown

dropdownShown(toolName) { return Menú por ${toolName} es visible; }

dropdownHidden

function (toolName) { return Menú por ${toolName} fue ocultado; }

zoomLevel

(zoomPercent) => Zoom: ${zoomPercent}%

colorChangedAnnouncement

function (color) { return Color fue cambiado a ${color}; }

imageSize

(size, units) => Tamaño del imagen: ${size} ${units}

errorImageHasZeroSize

No response

imageLoadError

(message) => Error cargando imagen: ${message}

penTool

function (penId) { return Lapiz ${penId}; }

selectionTool

Selecciona

selectAllTool

No response

eraserTool

Borrador

touchPanTool

Instrumento de mover la pantalla con un dedo

twoFingerPanZoomTool

No response

undoRedoTool

No response

rightClickDragPanTool

No response

pipetteTool

No response

keyboardPanZoom

Mover la pantalla con el teclado

textTool

Texto

enterTextToInsert

Entra texto

changeTool

No response

pasteHandler

No response

soundExplorer

No response

disableAccessibilityExploreTool

No response

enableAccessibilityExploreTool

No response

soundExplorerUsageAnnouncement

No response

findLabel

No response

toNextMatch

No response

closeDialog

No response

findDialogShown

No response

findDialogHidden

No response

focusedFoundText

(matchIdx, totalMatches) => Viewing match ${matchIdx} of ${totalMatches}

anyDevicePanning

Mover la pantalla con todo dispotivo

copied

(count, description) => Copied ${count} ${description}

pasted

(count, description) => Pasted ${count} ${description}

toolEnabledAnnouncement

(toolName) => ${toolName} enabled

toolDisabledAnnouncement

(toolName) => ${toolName} disabled

updatedViewport

No response

transformedElements

(elemCount) => Transformed ${elemCount} element${elemCount === 1 ? '' : 's'}

resizeOutputCommand

(newSize) => Resized image to ${newSize.w}x${newSize.h}

enabledAutoresizeOutputCommand

No response

disabledAutoresizeOutputCommand

No response

addElementAction

(componentDescription) => Added ${componentDescription}

eraseAction

(componentDescription, numElems) => Erased ${numElems} ${componentDescription}

duplicateAction

(componentDescription, numElems) => Duplicated ${numElems} ${componentDescription}

unionOf

(actionDescription, actionCount) => Union: ${actionCount} ${actionDescription}

inverseOf

(actionDescription) => Inverse of ${actionDescription}

elements

No response

erasedNoElements

No response

duplicatedNoElements

No response

rotatedBy

(degrees) => Rotated by ${Math.abs(degrees)} degrees ${degrees < 0 ? 'clockwise' : 'counter-clockwise'}

movedLeft

No response

movedUp

No response

movedDown

No response

movedRight

No response

zoomedOut

No response

zoomedIn

No response

selectedElements

(count) => Selected ${count} element${count === 1 ? '' : 's'}

unlabeledImageNode

No response

stroke

No response

svgObject

No response

emptyBackground

No response

gridBackground

No response

filledBackgroundWithColor

(color) => Filled background (${color})

text

(text) => Text object: ${text}

imageNode

(label) => Image: ${label}

restyledElement

(elementDescription) => Restyled ${elementDescription}

pathNodeCount

(count) => There are ${count} visible path objects.

textNodeCount

(count) => There are ${count} visible text nodes.

imageNodeCount

(nodeCount) => There are ${nodeCount} visible image nodes.

textNode

(content) => Text: ${content}

rerenderAsText

Redibuja la pantalla al texto

accessibilityInputInstructions

No response

loading

(percentage) => Cargando: ${percentage}%...

imageEditor

Editor de dibujos

doneLoading

El cargado terminó

undoAnnouncement

(commandDescription) => ${commandDescription} fue deshecho

redoAnnouncement

(commandDescription) => ${commandDescription} fue rehecho

Additional information

No response

no need for save exit words on large screens

Hey, first of all nicely done. The latency is just astronimical and not at all worse than samsung notes (which IS the best pen note taking app). I hope you understand the implications when joplin and js draw are tightly integrated, namely best overall note taking app for every group of people.

But now to the issue.
I see the design choice to have the save and exit text when having a small screen
image
, but there is no need for that in bigger screen where everything is on one line and therefore lots of space is used by just text. just the icons will be enough.

I now see that that is already the case when the text does not fit
image

All right then, then the solution is alright i suppose even though the text is not needed even if the screen is big enough. We should better add more toolbars

P.S. Is it alright if i report the joplin js draw bugs/features here?

Testing: Translation templates

Language

es

pen

Lapiz

eraser

Borrador

select

Selecciona

handTool

Mover

zoom

No response

image

Imagen

reformatSelection

No response

inputAltText

No response

chooseFile

No response

dragAndDropHereOrBrowse

No response

submit

No response

cancel

No response

resetView

Reiniciar vista

thicknessLabel

Tamaño

colorLabel

No response

fontLabel

Fuente:

textSize

Tamaño

resizeImageToSelection

Redimensionar la imagen a lo que está seleccionado

deleteSelection

Borra la selección

duplicateSelection

Duplica la selección

exit

No response

save

No response

undo

Deshace

redo

Rehace

selectPenTip

Forma de dibuja

selectShape

No response

pickColorFromScreen

Selecciona un color de la pantalla

clickToPickColorAnnouncement

Haga un clic en la pantalla para seleccionar un color

colorSelectionCanceledAnnouncement

No response

selectionToolKeyboardShortcuts

No response

documentProperties

Fondo

backgroundColor

Color de fondo:

imageWidthOption

Ancho

imageHeightOption

Alto

useGridOption

No response

enableAutoresizeOption

No response

toggleOverflow

Más

about

No response

inputStabilization

No response

touchPanning

Mover la pantalla con un dedo

roundedTipPen

Lapiz Redondeado

flatTipPen

No response

arrowPen

Flecha

linePen

Línea

outlinedRectanglePen

Rectángulo con nada más que un borde

filledRectanglePen

Rectángulo sin borde

outlinedCirclePen

No response

lockRotation

Bloquea rotación

paste

No response

closeSidebar

(toolName) => `Close sidebar for ${toolName}`

dropdownShown

dropdownShown(toolName) { return `Menú por ${toolName} es visible`; }

dropdownHidden

function (toolName) { return `Menú por ${toolName} fue ocultado`; }

zoomLevel

(zoomPercent) => `Zoom: ${zoomPercent}%`

colorChangedAnnouncement

function (color) { return `Color fue cambiado a ${color}`; }

imageSize

(size, units) => `Tamaño del imagen: ${size} ${units}`

errorImageHasZeroSize

No response

imageLoadError

(message) => `Error cargando imagen: ${message}`

penTool

function (penId) { return `Lapiz ${penId}`; }

selectionTool

Selecciona

selectAllTool

No response

eraserTool

Borrador

touchPanTool

Instrumento de mover la pantalla con un dedo

twoFingerPanZoomTool

No response

undoRedoTool

No response

rightClickDragPanTool

No response

pipetteTool

No response

keyboardPanZoom

Mover la pantalla con el teclado

textTool

Texto

enterTextToInsert

Entra texto

changeTool

No response

pasteHandler

No response

soundExplorer

No response

disableAccessibilityExploreTool

No response

enableAccessibilityExploreTool

No response

soundExplorerUsageAnnouncement

No response

findLabel

No response

toNextMatch

No response

closeDialog

No response

findDialogShown

No response

findDialogHidden

No response

focusedFoundText

(matchIdx, totalMatches) => `Viewing match ${matchIdx} of ${totalMatches}`

anyDevicePanning

Mover la pantalla con todo dispotivo

copied

(count, description) => `Copied ${count} ${description}`

pasted

(count, description) => `Pasted ${count} ${description}`

toolEnabledAnnouncement

(toolName) => `${toolName} enabled`

toolDisabledAnnouncement

(toolName) => `${toolName} disabled`

updatedViewport

No response

transformedElements

(elemCount) => `Transformed ${elemCount} element${elemCount === 1 ? '' : 's'}`

resizeOutputCommand

(newSize) => `Resized image to ${newSize.w}x${newSize.h}`

enabledAutoresizeOutputCommand

No response

disabledAutoresizeOutputCommand

No response

addElementAction

(componentDescription) => `Added ${componentDescription}`

eraseAction

(componentDescription, numElems) => `Erased ${numElems} ${componentDescription}`

duplicateAction

(componentDescription, numElems) => `Duplicated ${numElems} ${componentDescription}`

unionOf

(actionDescription, actionCount) => `Union: ${actionCount} ${actionDescription}`

inverseOf

(actionDescription) => `Inverse of ${actionDescription}`

elements

No response

erasedNoElements

No response

duplicatedNoElements

No response

rotatedBy

(degrees) => `Rotated by ${Math.abs(degrees)} degrees ${degrees < 0 ? 'clockwise' : 'counter-clockwise'}`

movedLeft

No response

movedUp

No response

movedDown

No response

movedRight

No response

zoomedOut

No response

zoomedIn

No response

selectedElements

(count) => `Selected ${count} element${count === 1 ? '' : 's'}`

unlabeledImageNode

No response

stroke

No response

svgObject

No response

emptyBackground

No response

gridBackground

No response

filledBackgroundWithColor

(color) => `Filled background (${color})`

text

(text) => `Text object: ${text}`

imageNode

(label) => `Image: ${label}`

restyledElement

(elementDescription) => `Restyled ${elementDescription}`

pathNodeCount

(count) => `There are ${count} visible path objects.`

textNodeCount

(count) => `There are ${count} visible text nodes.`

imageNodeCount

(nodeCount) => `There are ${nodeCount} visible image nodes.`

textNode

(content) => `Text: ${content}`

rerenderAsText

Redibuja la pantalla al texto

accessibilityInputInstructions

No response

loading

(percentage) => `Cargando: ${percentage}%...`

imageEditor

Editor de dibujos

doneLoading

El cargado terminó

undoAnnouncement

(commandDescription) => `${commandDescription} fue deshecho`

redoAnnouncement

(commandDescription) => `${commandDescription} fue rehecho`

Additional information

No response

Set some features programatically: Grid

I can't seem to find a way to turn on the Grid by default or programatically.

I'm using
editor.dispatchNoAnnounce(editor.image.setAutoresizeEnabled(true), false);
in order to set Autoresize, but there is no setGridEnabled()

Also checking on the BackgroundStyle, I've seen a getBackgroundStyle but not a set...

Am I completely wrong? When not, it would be nice to have such function, either in the settings like gridEnabled: true or with a dispach like editor.dispatchNoAnnounce(editor.image.setGridEnabled(true), false);.

Cheers,
Nick

trouble on surface pro 7

Describe the bug

When I draw a path on surface pro with surface pen, last path disappears. However with my finger I draw a path, last path remains, everything is good

Platform

  • OS: Win11
  • Browser: Joplin
  • Version: 2.11.11

Make features more discoverable

Usability issue

Currently, it's difficult to discover some features. For example, disabling touchscreen drawing. It has been commented that the autocorrect feature may also be difficult to discover.

Proposed solutions

  1. Add tooltips to buttons that explain their use
  2. Add an information icon that shows all tooltips at once: screenshot (i) icon highlighted, tooltips shown for different actions (or perhaps allows navigating between tooltips).
  3. Make the information icon instead show help for one tool/button at a time (and allow navigating between help items).

[Translation]: <language>

Language

Spanish

pen

No response

eraser

No response

select

No response

handTool

No response

zoom

No response

resetView

No response

thicknessLabel

No response

colorLabel

No response

fontLabel

No response

resizeImageToSelection

No response

deleteSelection

No response

duplicateSelection

No response

undo

No response

redo

No response

selectObjectType

No response

pickColorFromScreen

No response

clickToPickColorAnnouncement

No response

selectionToolKeyboardShortcuts

No response

touchPanning

No response

freehandPen

No response

pressureSensitiveFreehandPen

No response

arrowPen

Flecha

linePen

Línea

outlinedRectanglePen

No response

filledRectanglePen

No response

lockRotation

No response

dropdownShown

No response

dropdownHidden

No response

zoomLevel

No response

colorChangedAnnouncement

No response

penTool

No response

selectionTool

No response

eraserTool

No response

touchPanTool

No response

twoFingerPanZoomTool

No response

undoRedoTool

No response

rightClickDragPanTool

No response

pipetteTool

No response

keyboardPanZoom

No response

textTool

No response

enterTextToInsert

No response

changeTool

No response

pasteHandler

No response

anyDevicePanning

No response

copied

No response

pasted

No response

toolEnabledAnnouncement

No response

toolDisabledAnnouncement

No response

updatedViewport

No response

transformedElements

No response

resizeOutputCommand

No response

addElementAction

No response

eraseAction

No response

duplicateAction

No response

unionOf

No response

inverseOf

No response

elements

No response

erasedNoElements

No response

duplicatedNoElements

No response

rotatedBy

No response

movedLeft

No response

movedUp

No response

movedDown

No response

movedRight

No response

zoomedOut

No response

zoomedIn

No response

selectedElements

No response

unlabeledImageNode

No response

stroke

No response

svgObject

No response

text

No response

imageNode

No response

pathNodeCount

No response

textNodeCount

No response

imageNodeCount

No response

textNode

No response

rerenderAsText

No response

accessibilityInputInstructions

No response

loading

No response

imageEditor

No response

doneLoading

No response

undoAnnouncement

No response

redoAnnouncement

(commandDescription) => ${commandDescription} fue rehecho

Additional information

This is a test of the new translation template.

Update Spanish localization (es)

Language

es

pen

Lapiz

eraser

Borrador

select

Selecciona

handTool

Mover

zoom

No response

image

Imagen

reformatSelection

No response

inputAltText

Texto alternativo

decreaseImageSize

No response

resetImage

Reiniciar

chooseFile

Seleccionar archivo

dragAndDropHereOrBrowse

No response

submit

No response

cancel

Cancelar

resetView

Reiniciar vista

thicknessLabel

Tamaño

colorLabel

No response

fontLabel

Fuente:

textSize

Tamaño

resizeImageToSelection

Redimensionar la imagen a lo que está seleccionado

deleteSelection

Borra la selección

duplicateSelection

Duplica la selección

exit

Salir

save

Guardar

undo

Deshace

redo

Rehace

selectPenTip

Punta

selectShape

Forma

pickColorFromScreen

Selecciona un color de la pantalla

clickToPickColorAnnouncement

Haga un clic en la pantalla para seleccionar un color

colorSelectionCanceledAnnouncement

No response

selectionToolKeyboardShortcuts

No response

documentProperties

Fondo

backgroundColor

Color de fondo

imageWidthOption

Ancho

imageHeightOption

Alto

useGridOption

No response

enableAutoresizeOption

Redimensionar automático

toggleOverflow

Más

about

Info

inputStabilization

No response

strokeAutocorrect

No response

touchPanning

Mover la pantalla con un dedo

roundedTipPen

Lapiz Redondeado

flatTipPen

No response

arrowPen

Flecha

linePen

Línea

outlinedRectanglePen

Rectángulo delineado

filledRectanglePen

Rectángulo sin borde

outlinedCirclePen

No response

lockRotation

Bloquea rotación

paste

Pegar

closeSidebar

(toolName) => `Close sidebar for ${toolName}`

dropdownShown

(toolName) => `Menú por ${toolName} es visible`

dropdownHidden

(toolName) => { return `Menú por ${toolName} fue ocultado`; }

zoomLevel

(zoomPercent) => `Zoom: ${zoomPercent}%`

colorChangedAnnouncement

(color) => { return `Color fue cambiado a ${color}`; }

imageSize

(size, units) => `Tamaño del imagen: ${size} ${units}`

errorImageHasZeroSize

No response

imageLoadError

(message) => `Error cargando imagen: ${message}`

penTool

(penId) => `Lapiz ${penId}`

selectionTool

Selecciona

selectAllTool

No response

eraserTool

Borrador

touchPanTool

Instrumento de mover la pantalla con un dedo

twoFingerPanZoomTool

No response

undoRedoTool

Deshace/rehace

rightClickDragPanTool

No response

pipetteTool

Seleccione un color de la pantalla

keyboardPanZoom

Mover la pantalla con el teclado

autocorrectedTo

(strokeDescription) => `Autocorrected to ${strokeDescription}`

autocorrectionCanceled

No response

textTool

Texto

enterTextToInsert

Entra texto

changeTool

No response

pasteHandler

No response

soundExplorer

No response

disableAccessibilityExploreTool

No response

enableAccessibilityExploreTool

No response

soundExplorerUsageAnnouncement

No response

findLabel

Buscar

toNextMatch

Próxima

closeDialog

Cerrar

findDialogShown

No response

findDialogHidden

No response

focusedFoundText

No response

anyDevicePanning

Mover la pantalla con todo dispotivo

copied

(count, description) => `Copied ${count} ${description}`

pasted

(count, description) => `Pasted ${count} ${description}`

toolEnabledAnnouncement

(toolName) => `${toolName} fue activado`

toolDisabledAnnouncement

(toolName) => `${toolName} fue desactivado`

updatedViewport

No response

transformedElements

No response

resizeOutputCommand

(newSize) => `Tamaño de imagen fue cambiado a ${newSize.w}x${newSize.h}`

enabledAutoresizeOutputCommand

No response

disabledAutoresizeOutputCommand

No response

addElementAction

No response

eraseAction

(componentDescription, numElems) => `Borrado: ${numElems} ${componentDescription}`

duplicateAction

No response

unionOf

No response

inverseOf

No response

elements

No response

erasedNoElements

No response

duplicatedNoElements

No response

rotatedBy

No response

movedLeft

No response

movedUp

No response

movedDown

No response

movedRight

No response

zoomedOut

No response

zoomedIn

No response

selectedElements

No response

unlabeledImageNode

No response

stroke

No response

svgObject

No response

emptyBackground

No response

gridBackground

No response

filledBackgroundWithColor

No response

text

No response

imageNode

No response

restyledElement

No response

pathNodeCount

No response

textNodeCount

No response

imageNodeCount

No response

textNode

No response

rerenderAsText

Redibuja la pantalla al texto

accessibilityInputInstructions

No response

loading

(percentage) => `Cargando: ${percentage}%...`

imageEditor

Editor de dibujos

doneLoading

El cargado terminó

undoAnnouncement

(commandDescription) => `${commandDescription} fue deshecho`

redoAnnouncement

(commandDescription) => `${commandDescription} fue rehecho`

softwareLibraries

No response

developerInformation

No response

Additional information

No response

Rework toolbar dropdowns

Summary

Currently, toolbar dropdowns in js-draw look the same on all platforms. This is the current appearance (and can be seen here):
Screenshot: rectangular dropdown is positioned directly below a toolbar button
Some users have found these dropdowns initially unintuitive (e.g. discovering that they exist) and their design differs significantly from that of other apps.

The redesign will likely have different dropdown layouts for different platforms (e.g. small screen width vs large screen width).

To-dos

  • Redesign dropdowns/toolbar on small-width screens
  • Redesign dropdowns/toolbar on large-width screens
  • Implement draft versions of designs (branch)
    • Refactoring to allow multiple different types of toolbar layouts (without significantly changing API).
    • Implement redesign possibilities (see below)
  • Receive and implement feedback on drafts

Design drafts

Note: The below sketches don't include the exact icons/colors/theming I plan to use.

Redesign possibility 1: Sidebars

On large screens, one option is to display tool options in a sidebar, rather than a dropdown. For example,

Design mockup: Text tool options shown in a sidebar, rather than a dropdown

While this is a closer experience to many other applications and may be easier to navigate to, it is unclear whether the pan tool/page dropdown should have a similar sidebar or a completely different design.

At present, the "pan" dropdown looks like this:
screenshot: pan tool dropdown: A list of buttons with icons

The "page" dropdown looks like this:
screenshot: page tool dropdown

The pan dropdown might, with a sidebar, look like this:

Image

Redesign possibility 2: Drawer-like menu from the bottom of the screen

On narrow-width screens, it may make sense to use a menu that comes up from the bottom of the screen:

Drawer coming up from bottom of screen

Tapping on a tool twice or long-pressing could show the dropdown.

Sometimes difficult to resize the selection if holding the Control key

Describe the bug

Holding Ctrl, then dragging a selection tool handle, can be difficult/impossible. For context, Ctrl is the default "snap-to-grid" shortcut and snaps input events (including mouse-down events) to a grid.

To Reproduce

  1. Draw something
  2. Switch to the selection tool.
  3. Select everything
  4. Hold ctrl
  5. Try to drag the resize handle

Observed behavior

Depending on the location of the resize handle, the stroke is dragged instead.

Expected behavior

The stroke should be resized.

Screenshots

If applicable, add screenshots to help explain the issue.

Platform

  • Browser: Firefox, Chrome, Safari
  • Library version: af1a27f

Highlighter: Strokes with sharp corners have corners with double lines

Edit: This only happens with the non-default, flat-edged pen.

Describe the bug

Strokes (thick strokes especially) often have rendering issues, particularly double-lines at sharp-angled joints.

To Reproduce

Steps to reproduce the behavior:

  1. Switch a thick pen tool
  2. Write a 'T' quickly, with one stroke
  3. Zoom in on the left side of the T

Observed behavior

screenshot of stroke with two lines at a sharp joint

Platform

  • Browser: Firefox, Chrome, Safari
  • Version: 0.5.0

Multi-line text objects saved as multiple single-line text objects

Describe the bug

Multi-line text objects are saved/loaded as several single-line text objects.

Steps to reproduce

  1. Create a text object with the text tool (use shift+enter to create multiple lines)
  2. Save
  3. Load from the save
  4. Try to edit the text objects.

Platform

  • Version: 0.6.1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.