Compare commits
426 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 198992f02b | |||
| a830820d45 | |||
| 211c2c5b4c | |||
| ebcd8535ed | |||
| 34fd9d60d7 | |||
| 60e831851b | |||
| 40a4146b67 | |||
| e466771826 | |||
| 06042fa7ee | |||
| abdf12bcd0 | |||
| 57a310dfb5 | |||
| 06fb8d3d7c | |||
| f46dd72595 | |||
| da36bc650d | |||
| 90d458c089 | |||
| 93cf69a588 | |||
| 7dc916e3dc | |||
| 460036e324 | |||
| c4a527ee17 | |||
| 45d11181c9 | |||
| bc9fccd61b | |||
| a83cfaae6f | |||
| 795274f9f0 | |||
| f51ac29fbe | |||
| e1698dd3aa | |||
| a39587c845 | |||
| 76f004bf99 | |||
| 5c0f4fdd42 | |||
| 9d157d7009 | |||
| 84e5373a9a | |||
| 107023393c | |||
| 19f2c741a9 | |||
| a3d000ee71 | |||
| 6ff96ef739 | |||
| e34139986c | |||
| 30801b718a | |||
| 606b05eb17 | |||
| 625fad6c1b | |||
| 9ebafb63a6 | |||
| 4d087cbbd9 | |||
| 539f6807e3 | |||
| 20b2604c68 | |||
| 0781ac6ad9 | |||
| 22b8282812 | |||
| 50500f57f8 | |||
| 9a0f8167eb | |||
| 9564931b69 | |||
| 83a3ed78b4 | |||
| 8d240c6f66 | |||
| 132a8d3cf8 | |||
| b2998238f7 | |||
| a72f4e1689 | |||
| dc68825d2b | |||
| c49cfc3bde | |||
| df1566a6ee | |||
| e89685b931 | |||
| b292d2ecbc | |||
| 4e8d015c6d | |||
| f520d6839c | |||
| 1dddcf1633 | |||
| db7d73c65e | |||
| 5371a13749 | |||
| 73ee1552b7 | |||
| aaa71dad68 | |||
| 365320fdc6 | |||
| ca05af6aee | |||
| 3de073dfa2 | |||
| 1f73461abd | |||
| b7bc95a216 | |||
| f11580cc71 | |||
| bb5c9439c7 | |||
| cec234157b | |||
| 69cd706970 | |||
| fa9ca4598d | |||
| 9c88cc2305 | |||
| 8817c4ed5e | |||
| f65bdb0a90 | |||
| 99ce84a9b8 | |||
| 5b15b45bec | |||
| 0573bc959b | |||
| b950747454 | |||
| 60609026c9 | |||
| fd5affb040 | |||
| 5f6dd3dc91 | |||
| 62a178758b | |||
| c9218fdb10 | |||
| fd13af7747 | |||
| c310a50fd5 | |||
| aba5b98285 | |||
| 53acd4e31b | |||
| 38837620e0 | |||
| ec1c4e8ce3 | |||
| 27a3d5a36d | |||
| 038a44b8ef | |||
| 56bfebb15c | |||
| 9e14480293 | |||
| 1b24d6def7 | |||
| 5cca65323f | |||
| 32d92bcef5 | |||
| c6c7040cac | |||
| c6419e54db | |||
| 1ce70c7022 | |||
| af3d94064a | |||
| 0750f61536 | |||
| 979830edf6 | |||
| 7eea5d5ec4 | |||
| eef72ca4fb | |||
| d0e25ccec2 | |||
| cb616e2957 | |||
| 80d466cf7a | |||
| d0383d0ce3 | |||
| d75222110b | |||
| e27e4e0e2d | |||
| 6057a40665 | |||
| e29c03554c | |||
| 3643bf82d0 | |||
| 50cfcc26cc | |||
| d5fb4305a3 | |||
| 970a1aa720 | |||
| e3d305ddc2 | |||
| 04389e8408 | |||
| 29dbc53a56 | |||
| 8acc174353 | |||
| 6985e75394 | |||
| 0b65b2cc63 | |||
| f6ea4bf135 | |||
| a5b37fabea | |||
| 43476864ad | |||
| e29436d8db | |||
| f0b6ace0fc | |||
| d0a5264b1b | |||
| 841ef74430 | |||
| 3f7d7f79de | |||
| a30d454e5f | |||
| d3471352ed | |||
| de9c8d9fc3 | |||
| 1ab1db08a8 | |||
| 22a72427ec | |||
| 74428d8091 | |||
| 657929ea6f | |||
| 3a0e6aa70c | |||
| 743fd2d38a | |||
| 3b89c89a52 | |||
| f0a80e9f78 | |||
| 5e3dd21a10 | |||
| a96134fa81 | |||
| 8dd6dd666c | |||
| 3a1fc51e44 | |||
| d83742c5eb | |||
| 83da4bd876 | |||
| c08a3a4d65 | |||
| 65e20609d7 | |||
| 5d62d50e4f | |||
| 218acae143 | |||
| 35e618cdb6 | |||
| e1f14c081c | |||
| cd3165b180 | |||
| 94da04d9f9 | |||
| a112d5ea41 | |||
| 77ba23407c | |||
| 4ea2007557 | |||
| 42d2692b2f | |||
| f7c7632ca6 | |||
| 61737f9c1b | |||
| 24d176ba2c | |||
| f363fc071b | |||
| 7221028010 | |||
| fc1762d0fd | |||
| 0581fc1eaf | |||
| 284ac616e1 | |||
| 631ff71965 | |||
| fb95a77055 | |||
| 0d57e9f628 | |||
| 7a24f13e81 | |||
| 8f6e1d942d | |||
| b21c6abbe4 | |||
| 8757a9ddc4 | |||
| f7b3497dee | |||
| 0e17d69535 | |||
| ff6ec195bd | |||
| dd8637e125 | |||
| 6d9257310c | |||
| 3d3f96880d | |||
| 715399b558 | |||
| 47c254216b | |||
| b818b61d8b | |||
| 99757b1a06 | |||
| fe547a6ada | |||
| b6cb835cd4 | |||
| 917079900a | |||
| a3304507a1 | |||
| 453d934cd9 | |||
| cf73a91567 | |||
| 368ea9dd7f | |||
| 0bdca3493b | |||
| f69095d2bd | |||
| f5df6b9e4c | |||
| 0d285b3eec | |||
| be98b5c7cf | |||
| 013cc37a47 | |||
| 82f3fa6c57 | |||
| 669f8a2b59 | |||
| 0518fa5beb | |||
| e48b742ee1 | |||
| d4c45dc9be | |||
| d728887146 | |||
| 240e1b0492 | |||
| 4cb28e838f | |||
| 5823570530 | |||
| b832b44413 | |||
| 5c7123e77b | |||
| 36b220a067 | |||
| d03c2eb963 | |||
| e01199e6b8 | |||
| 9c516f1e0a | |||
| 6566456832 | |||
| 60936c48ca | |||
| 43be14dc93 | |||
| 526d2c52f3 | |||
| fcb69c36e6 | |||
| 53a4d76e3b | |||
| 0710d40919 | |||
| a07cd1b0e8 | |||
| 4a55ab6bab | |||
| 6257bdbfb6 | |||
| 3f377c9d69 | |||
| 176e11bac8 | |||
| 84c89018d9 | |||
| 6cac1c8a04 | |||
| 1e996e4d94 | |||
| d028157a5c | |||
| 2934b8c308 | |||
| c6f08724eb | |||
| 976da91fe7 | |||
| 0b42b49d9e | |||
| 3a472aa6fc | |||
| 6a716adea6 | |||
| abb33bf686 | |||
| 0463c18f02 | |||
| fd83e44c6f | |||
| 2d0d6d6c1b | |||
| ab39228fc9 | |||
| 6e9085eee7 | |||
| eac4fa5b57 | |||
| ef40b72eb0 | |||
| d1cff91b75 | |||
| 437595fa65 | |||
| e1f5b9f138 | |||
| 8bf7fae439 | |||
| 503fd4c598 | |||
| e99df11729 | |||
| 39b9224c3c | |||
| 38cf2fb51e | |||
| 333a2ee6fc | |||
| 728bb66eb7 | |||
| 49ea45aa5d | |||
| cf5418b128 | |||
| 9e0eb0f541 | |||
| d506a822ff | |||
| 0fb50e07c0 | |||
| c2edd7c6f4 | |||
| bf7aafb77d | |||
| a3b9763dcd | |||
| baf93453c3 | |||
| d9b9dc783e | |||
| dd4343d48c | |||
| fdd021574c | |||
| 652b0f60f5 | |||
| 902c8eaf56 | |||
| 7e9a7cbd26 | |||
| 9d04681dc5 | |||
| b0bfc7af20 | |||
| ba2828914d | |||
| 791b63148b | |||
| 9ff150bdc2 | |||
| 527b2658c1 | |||
| 7b8983c75a | |||
| c4067bfe2f | |||
| 46abd7bc96 | |||
| 3ba91352ed | |||
| 0e57d55451 | |||
| d4e6e3cae3 | |||
| c6395ae166 | |||
| 2d1aeee971 | |||
| c9455a0de4 | |||
| 9f944db928 | |||
| 170bf35513 | |||
| e6e8349a45 | |||
| d632694c9b | |||
| cff4ca670d | |||
| eaabc26428 | |||
| 0bcc2360a9 | |||
| f3da2a95ed | |||
| bec4653dcb | |||
| a2e948d080 | |||
| 6b7da271b5 | |||
| 65c7c1815f | |||
| 8c019fe3fd | |||
| 64466783de | |||
| ce3774e8f2 | |||
| 5dd19a04c2 | |||
| 44769fe876 | |||
| 4947ec612c | |||
| 24b78a1332 | |||
| ee4f0a43fc | |||
| cfd9306c57 | |||
| 8a3048d4a7 | |||
| d7138d86ea | |||
| b0644a6d3f | |||
| 430adfe6f4 | |||
| ceb6b47c17 | |||
| 54973ba281 | |||
| 157b1911e1 | |||
| 482beddd67 | |||
| 904828940d | |||
| d0990c63db | |||
| c86d22be7f | |||
| 7c03c458a1 | |||
| 60b275880d | |||
| 8029e68e17 | |||
| c3e9430577 | |||
| cae9d2bcbd | |||
| 2874f9e48c | |||
| 0b3a5e7cc4 | |||
| 7ea3229e17 | |||
| b0404b10b6 | |||
| 9d34d4fcd4 | |||
| fe310acebd | |||
| d7c7236ee8 | |||
| eefc3b9408 | |||
| cc67f9d544 | |||
| eb959128ac | |||
| e2e13bba0b | |||
| 041f012f72 | |||
| dbd8cc8d6f | |||
| 9fbac79d11 | |||
| ef915b7427 | |||
| c5b913ad9f | |||
| f66c943720 | |||
| 9c0edf1cb9 | |||
| 9112c61edb | |||
| f58a84a857 | |||
| 626e846d10 | |||
| feacdcd156 | |||
| e1fbf09310 | |||
| dc521a62c1 | |||
| f0bbb7614e | |||
| 62b35ab86c | |||
| 2ea8c308e4 | |||
| 0a13e3a344 | |||
| 540809f2a4 | |||
| 467d9bd08c | |||
| 13e8374bea | |||
| b96dd6d332 | |||
| 32d77172cc | |||
| 9e23421d53 | |||
| c3d91aac44 | |||
| 99088550be | |||
| b9a171a2ef | |||
| 521d727b5b | |||
| b7d610fbbe | |||
| 1f33be6403 | |||
| 7ca28ae504 | |||
| d648d1147e | |||
| 869739b170 | |||
| ca09cc2830 | |||
| 5d1e8448bc | |||
| 2076b3643a | |||
| cbe5581782 | |||
| be9fcec967 | |||
| 41f8203889 | |||
| 60be58033f | |||
| 7010f1af88 | |||
| edd1510183 | |||
| 5926023ab1 | |||
| 7adc3d163f | |||
| 124ecebca6 | |||
| 8d8fdc2985 | |||
| 0691617028 | |||
| 3f181f7fa7 | |||
| 00f2600cbb | |||
| 4274935c62 | |||
| 6209445644 | |||
| d9636c4101 | |||
| fc1c3a3985 | |||
| 2c7737ed9b | |||
| c4dcd3a5d2 | |||
| f0a37029bc | |||
| 2567526103 | |||
| a10f7c10ae | |||
| 60dc788254 | |||
| 34ab5746b4 | |||
| 6c7b8f4bf4 | |||
| 7a79d0306c | |||
| d70108dcfa | |||
| c2fc95116a | |||
| 947a11bac2 | |||
| 87336c115e | |||
| 6041c34aeb | |||
| 184d9ca6e8 | |||
| 3a844186a7 | |||
| cc262ce5d5 | |||
| 802b01562c | |||
| 6f2b43ff0c | |||
| 461f327887 | |||
| a33a643a2f | |||
| 91f7b38a39 | |||
| 998a296ca2 | |||
| 0afec1bf5c | |||
| 2436e8498a | |||
| 521362a532 | |||
| ae5a2dee54 | |||
| bf62bce4cb | |||
| 92c5acb960 | |||
| d4ce7b067a | |||
| 214e68ce03 | |||
| 19cb9ab30e | |||
| 3d097669f8 | |||
| ede77bd6ac | |||
| 05dde8f0d1 | |||
| d11453ae15 | |||
| 2d3577bb43 | |||
| 4258aeac53 | |||
| 4f772d77a4 | |||
| 6f57935419 | |||
| 61c69915da |
@@ -1,9 +1,3 @@
|
||||
declare global {
|
||||
interface Window {
|
||||
debug: typeof Debug;
|
||||
}
|
||||
}
|
||||
|
||||
const lessPrecise = (num: number, precision = 5) =>
|
||||
parseFloat(num.toPrecision(precision));
|
||||
|
||||
@@ -157,6 +151,70 @@ export class Debug {
|
||||
return ret;
|
||||
};
|
||||
};
|
||||
|
||||
private static CHANGED_CACHE: Record<string, Record<string, unknown>> = {};
|
||||
|
||||
public static logChanged(name: string, obj: Record<string, unknown>) {
|
||||
const prev = Debug.CHANGED_CACHE[name];
|
||||
|
||||
Debug.CHANGED_CACHE[name] = obj;
|
||||
|
||||
if (!prev) {
|
||||
return;
|
||||
}
|
||||
|
||||
const allKeys = new Set([...Object.keys(prev), ...Object.keys(obj)]);
|
||||
const changed: Record<string, { prev: unknown; next: unknown }> = {};
|
||||
|
||||
for (const key of allKeys) {
|
||||
const prevVal = prev[key];
|
||||
const nextVal = obj[key];
|
||||
if (!deepEqual(prevVal, nextVal)) {
|
||||
changed[key] = { prev: prevVal, next: nextVal };
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(changed).length > 0) {
|
||||
console.info(`[${name}] changed:`, changed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deepEqual(a: unknown, b: unknown): boolean {
|
||||
if (Object.is(a, b)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
a === null ||
|
||||
b === null ||
|
||||
typeof a !== "object" ||
|
||||
typeof b !== "object"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(a) !== Array.isArray(b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const keysA = Object.keys(a as Record<string, unknown>);
|
||||
const keysB = Object.keys(b as Record<string, unknown>);
|
||||
|
||||
if (keysA.length !== keysB.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const key of keysA) {
|
||||
if (
|
||||
!deepEqual(
|
||||
(a as Record<string, unknown>)[key],
|
||||
(b as Record<string, unknown>)[key],
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
//@ts-ignore
|
||||
window.debug = Debug;
|
||||
@@ -240,22 +240,21 @@ export const DEFAULT_ELEMENT_BACKGROUND_COLOR_PALETTE = {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// !!!MUST BE WITHOUT GRAY, TRANSPARENT AND BLACK!!!
|
||||
export const getAllColorsSpecificShade = (index: 0 | 1 | 2 | 3 | 4) =>
|
||||
[
|
||||
// 2nd row
|
||||
COLOR_PALETTE.cyan[index],
|
||||
COLOR_PALETTE.blue[index],
|
||||
COLOR_PALETTE.violet[index],
|
||||
COLOR_PALETTE.grape[index],
|
||||
COLOR_PALETTE.pink[index],
|
||||
export const getAllColorsSpecificShade = (index: 0 | 1 | 2 | 3 | 4) => [
|
||||
// 2nd row
|
||||
COLOR_PALETTE.cyan[index],
|
||||
COLOR_PALETTE.blue[index],
|
||||
COLOR_PALETTE.violet[index],
|
||||
COLOR_PALETTE.grape[index],
|
||||
COLOR_PALETTE.pink[index],
|
||||
|
||||
// 3rd row
|
||||
COLOR_PALETTE.green[index],
|
||||
COLOR_PALETTE.teal[index],
|
||||
COLOR_PALETTE.yellow[index],
|
||||
COLOR_PALETTE.orange[index],
|
||||
COLOR_PALETTE.red[index],
|
||||
] as const;
|
||||
// 3rd row
|
||||
COLOR_PALETTE.green[index],
|
||||
COLOR_PALETTE.teal[index],
|
||||
COLOR_PALETTE.yellow[index],
|
||||
COLOR_PALETTE.orange[index],
|
||||
COLOR_PALETTE.red[index],
|
||||
];
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// other helpers
|
||||
|
||||
@@ -12,3 +12,4 @@ export * from "./url";
|
||||
export * from "./utils";
|
||||
export * from "./emitter";
|
||||
export * from "./editorInterface";
|
||||
export { Debug } from "../debug";
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { average } from "@excalidraw/math";
|
||||
|
||||
import type { GlobalCoord } from "@excalidraw/math";
|
||||
|
||||
import type { FontFamilyValues, FontString } from "@excalidraw/element/types";
|
||||
|
||||
import type {
|
||||
@@ -441,7 +443,7 @@ export const viewportCoordsToSceneCoords = (
|
||||
const x = (clientX - offsetLeft) / zoom.value - scrollX;
|
||||
const y = (clientY - offsetTop) / zoom.value - scrollY;
|
||||
|
||||
return { x, y };
|
||||
return { x, y } as GlobalCoord;
|
||||
};
|
||||
|
||||
export const sceneCoordsToViewportCoords = (
|
||||
@@ -1330,3 +1332,10 @@ export const setFeatureFlag = <F extends keyof FEATURE_FLAGS>(
|
||||
console.error("unable to set feature flag", e);
|
||||
}
|
||||
};
|
||||
|
||||
export const oneOf = <N extends string | number | symbol | null, H extends N>(
|
||||
needle: N,
|
||||
haystack: readonly H[],
|
||||
): needle is H => {
|
||||
return haystack.includes(needle as any);
|
||||
};
|
||||
|
||||
@@ -438,6 +438,8 @@ export class Scene {
|
||||
options: {
|
||||
informMutation: boolean;
|
||||
isDragging: boolean;
|
||||
isBindingEnabled?: boolean;
|
||||
isMidpointSnappingEnabled?: boolean;
|
||||
} = {
|
||||
informMutation: true,
|
||||
isDragging: false,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
KEYS,
|
||||
arrayToMap,
|
||||
getFeatureFlag,
|
||||
invariant,
|
||||
@@ -137,12 +136,6 @@ export const maxBindingDistance_simple = (zoom?: AppState["zoom"]): number => {
|
||||
);
|
||||
};
|
||||
|
||||
export const shouldEnableBindingForPointerEvent = (
|
||||
event: React.PointerEvent<HTMLElement>,
|
||||
) => {
|
||||
return !event[KEYS.CTRL_OR_CMD];
|
||||
};
|
||||
|
||||
export const isBindingEnabled = (appState: {
|
||||
isBindingEnabled: AppState["isBindingEnabled"];
|
||||
}): boolean => {
|
||||
@@ -177,8 +170,20 @@ export const bindOrUnbindBindingElement = (
|
||||
},
|
||||
);
|
||||
|
||||
bindOrUnbindBindingElementEdge(arrow, start, "start", scene);
|
||||
bindOrUnbindBindingElementEdge(arrow, end, "end", scene);
|
||||
bindOrUnbindBindingElementEdge(
|
||||
arrow,
|
||||
start,
|
||||
"start",
|
||||
scene,
|
||||
appState.isBindingEnabled,
|
||||
);
|
||||
bindOrUnbindBindingElementEdge(
|
||||
arrow,
|
||||
end,
|
||||
"end",
|
||||
scene,
|
||||
appState.isBindingEnabled,
|
||||
);
|
||||
if (start.focusPoint || end.focusPoint) {
|
||||
// If the strategy dictates a focus point override, then
|
||||
// update the arrow points to point to the focus point.
|
||||
@@ -221,12 +226,21 @@ const bindOrUnbindBindingElementEdge = (
|
||||
{ mode, element, focusPoint }: BindingStrategy,
|
||||
startOrEnd: "start" | "end",
|
||||
scene: Scene,
|
||||
shouldSnapToOutline = true,
|
||||
): void => {
|
||||
if (mode === null) {
|
||||
// null means break the binding
|
||||
unbindBindingElement(arrow, startOrEnd, scene);
|
||||
} else if (mode !== undefined) {
|
||||
bindBindingElement(arrow, element, mode, startOrEnd, scene, focusPoint);
|
||||
bindBindingElement(
|
||||
arrow,
|
||||
element,
|
||||
mode,
|
||||
startOrEnd,
|
||||
scene,
|
||||
focusPoint,
|
||||
shouldSnapToOutline,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -798,6 +812,7 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
||||
startDragged ? "start" : "end",
|
||||
elementsMap,
|
||||
appState.zoom,
|
||||
appState.isMidpointSnappingEnabled,
|
||||
) || globalPoint,
|
||||
}
|
||||
: { mode: null };
|
||||
@@ -842,6 +857,7 @@ const getBindingStrategyForDraggingBindingElementEndpoints_simple = (
|
||||
startDragged ? "end" : "start",
|
||||
elementsMap,
|
||||
appState.zoom,
|
||||
appState.isMidpointSnappingEnabled,
|
||||
) || otherEndpoint,
|
||||
}
|
||||
: { mode: undefined }
|
||||
@@ -1005,6 +1021,7 @@ export const bindBindingElement = (
|
||||
startOrEnd: "start" | "end",
|
||||
scene: Scene,
|
||||
focusPoint?: GlobalPoint,
|
||||
shouldSnapToOutline = true,
|
||||
): void => {
|
||||
const elementsMap = scene.getNonDeletedElementsMap();
|
||||
|
||||
@@ -1019,6 +1036,7 @@ export const bindBindingElement = (
|
||||
hoveredElement,
|
||||
startOrEnd,
|
||||
elementsMap,
|
||||
shouldSnapToOutline,
|
||||
),
|
||||
};
|
||||
} else {
|
||||
@@ -1352,6 +1370,7 @@ export const bindPointToSnapToElementOutline = (
|
||||
startOrEnd: "start" | "end",
|
||||
elementsMap: ElementsMap,
|
||||
customIntersector?: LineSegment<GlobalPoint>,
|
||||
isMidpointSnappingEnabled = true,
|
||||
): GlobalPoint => {
|
||||
const elbowed = isElbowArrow(arrowElement);
|
||||
const point = LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||
@@ -1391,13 +1410,9 @@ export const bindPointToSnapToElementOutline = (
|
||||
const isHorizontal = headingIsHorizontal(
|
||||
headingForPointFromElement(bindableElement, aabb, point),
|
||||
);
|
||||
const snapPoint = snapToMid(
|
||||
bindableElement,
|
||||
elementsMap,
|
||||
edgePoint,
|
||||
0.05,
|
||||
arrowElement,
|
||||
);
|
||||
const snapPoint = isMidpointSnappingEnabled
|
||||
? snapToMid(bindableElement, elementsMap, edgePoint, 0.05, arrowElement)
|
||||
: undefined;
|
||||
const resolved = snapPoint || point;
|
||||
const otherPoint = pointFrom<GlobalPoint>(
|
||||
isHorizontal ? bindableCenter[0] : resolved[0],
|
||||
@@ -1776,10 +1791,13 @@ export const updateBoundPoint = (
|
||||
);
|
||||
const otherArrowPoint = LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||
arrow,
|
||||
startOrEnd === "startBinding" ? -1 : 0,
|
||||
startOrEnd === "startBinding" ? 1 : -2,
|
||||
elementsMap,
|
||||
);
|
||||
const otherFocusPointOrArrowPoint = otherFocusPoint || otherArrowPoint;
|
||||
const otherFocusPointOrArrowPoint =
|
||||
arrow.points.length === 2
|
||||
? otherFocusPoint || otherArrowPoint
|
||||
: otherArrowPoint;
|
||||
const intersector =
|
||||
otherFocusPointOrArrowPoint &&
|
||||
lineSegment(focusPoint, otherFocusPointOrArrowPoint);
|
||||
@@ -1889,6 +1907,8 @@ export const calculateFixedPointForElbowArrowBinding = (
|
||||
hoveredElement: ExcalidrawBindableElement,
|
||||
startOrEnd: "start" | "end",
|
||||
elementsMap: ElementsMap,
|
||||
shouldSnapToOutline = true,
|
||||
isMidpointSnappingEnabled = true,
|
||||
): { fixedPoint: FixedPoint } => {
|
||||
const bounds = [
|
||||
hoveredElement.x,
|
||||
@@ -1896,12 +1916,20 @@ export const calculateFixedPointForElbowArrowBinding = (
|
||||
hoveredElement.x + hoveredElement.width,
|
||||
hoveredElement.y + hoveredElement.height,
|
||||
] as Bounds;
|
||||
const snappedPoint = bindPointToSnapToElementOutline(
|
||||
linearElement,
|
||||
hoveredElement,
|
||||
startOrEnd,
|
||||
elementsMap,
|
||||
);
|
||||
const snappedPoint = shouldSnapToOutline
|
||||
? bindPointToSnapToElementOutline(
|
||||
linearElement,
|
||||
hoveredElement,
|
||||
startOrEnd,
|
||||
elementsMap,
|
||||
undefined,
|
||||
isMidpointSnappingEnabled,
|
||||
)
|
||||
: LinearElementEditor.getPointAtIndexGlobalCoordinates(
|
||||
linearElement,
|
||||
startOrEnd === "start" ? 0 : -1,
|
||||
elementsMap,
|
||||
);
|
||||
const globalMidPoint = pointFrom(
|
||||
bounds[0] + (bounds[2] - bounds[0]) / 2,
|
||||
bounds[1] + (bounds[3] - bounds[1]) / 2,
|
||||
@@ -2447,21 +2475,37 @@ export const getArrowLocalFixedPoints = (
|
||||
];
|
||||
};
|
||||
|
||||
export const normalizeFixedPoint = <T extends FixedPoint | null>(
|
||||
export const isFixedPoint = (
|
||||
fixedPoint: any,
|
||||
): fixedPoint is FixedPointBinding["fixedPoint"] => {
|
||||
return (
|
||||
Array.isArray(fixedPoint) &&
|
||||
fixedPoint.length === 2 &&
|
||||
fixedPoint.every((coord) => Number.isFinite(coord))
|
||||
);
|
||||
};
|
||||
|
||||
export const normalizeFixedPoint = <T extends FixedPoint>(
|
||||
fixedPoint: T,
|
||||
): T extends null ? null : FixedPoint => {
|
||||
): FixedPoint => {
|
||||
if (!isFixedPoint(fixedPoint)) {
|
||||
return [0.5001, 0.5001];
|
||||
}
|
||||
|
||||
const EPSILON = 0.0001;
|
||||
|
||||
// Do not allow a precise 0.5 for fixed point ratio
|
||||
// to avoid jumping arrow heading due to floating point imprecision
|
||||
if (
|
||||
fixedPoint &&
|
||||
(Math.abs(fixedPoint[0] - 0.5) < 0.0001 ||
|
||||
Math.abs(fixedPoint[1] - 0.5) < 0.0001)
|
||||
Math.abs(fixedPoint[0] - 0.5) < EPSILON ||
|
||||
Math.abs(fixedPoint[1] - 0.5) < EPSILON
|
||||
) {
|
||||
return fixedPoint.map((ratio) =>
|
||||
Math.abs(ratio - 0.5) < 0.0001 ? 0.5001 : ratio,
|
||||
) as T extends null ? null : FixedPoint;
|
||||
Math.abs(ratio - 0.5) < EPSILON ? 0.5001 : ratio,
|
||||
) as FixedPoint;
|
||||
}
|
||||
return fixedPoint as any as T extends null ? null : FixedPoint;
|
||||
|
||||
return fixedPoint;
|
||||
};
|
||||
|
||||
type Side =
|
||||
|
||||
@@ -915,6 +915,8 @@ export const updateElbowArrowPoints = (
|
||||
},
|
||||
options?: {
|
||||
isDragging?: boolean;
|
||||
isBindingEnabled?: boolean;
|
||||
isMidpointSnappingEnabled?: boolean;
|
||||
},
|
||||
): ElementUpdate<ExcalidrawElbowArrowElement> => {
|
||||
if (arrow.points.length < 2) {
|
||||
@@ -1202,6 +1204,8 @@ const getElbowArrowData = (
|
||||
options?: {
|
||||
isDragging?: boolean;
|
||||
zoom?: AppState["zoom"];
|
||||
isBindingEnabled?: boolean;
|
||||
isMidpointSnappingEnabled?: boolean;
|
||||
},
|
||||
) => {
|
||||
const origStartGlobalPoint: GlobalPoint = pointTranslate<
|
||||
@@ -1215,7 +1219,7 @@ const getElbowArrowData = (
|
||||
|
||||
let hoveredStartElement = null;
|
||||
let hoveredEndElement = null;
|
||||
if (options?.isDragging) {
|
||||
if (options?.isDragging && options?.isBindingEnabled !== false) {
|
||||
const elements = Array.from(elementsMap.values());
|
||||
hoveredStartElement =
|
||||
getHoveredElement(
|
||||
@@ -1255,6 +1259,8 @@ const getElbowArrowData = (
|
||||
hoveredStartElement,
|
||||
elementsMap,
|
||||
options?.isDragging,
|
||||
options?.isBindingEnabled,
|
||||
options?.isMidpointSnappingEnabled,
|
||||
);
|
||||
const endGlobalPoint = getGlobalPoint(
|
||||
{
|
||||
@@ -1270,6 +1276,8 @@ const getElbowArrowData = (
|
||||
hoveredEndElement,
|
||||
elementsMap,
|
||||
options?.isDragging,
|
||||
options?.isBindingEnabled,
|
||||
options?.isMidpointSnappingEnabled,
|
||||
);
|
||||
const startHeading = getBindPointHeading(
|
||||
startGlobalPoint,
|
||||
@@ -2213,14 +2221,18 @@ const getGlobalPoint = (
|
||||
element?: ExcalidrawBindableElement | null,
|
||||
elementsMap?: ElementsMap,
|
||||
isDragging?: boolean,
|
||||
isBindingEnabled = true,
|
||||
isMidpointSnappingEnabled = true,
|
||||
): GlobalPoint => {
|
||||
if (isDragging) {
|
||||
if (element && elementsMap) {
|
||||
if (isBindingEnabled && element && elementsMap) {
|
||||
return bindPointToSnapToElementOutline(
|
||||
arrow,
|
||||
element,
|
||||
startOrEnd,
|
||||
elementsMap,
|
||||
undefined,
|
||||
isMidpointSnappingEnabled,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -359,11 +359,20 @@ export class LinearElementEditor {
|
||||
linearElementEditor,
|
||||
);
|
||||
|
||||
LinearElementEditor.movePoints(element, app.scene, positions, {
|
||||
startBinding: updates?.startBinding,
|
||||
endBinding: updates?.endBinding,
|
||||
moveMidPointsWithElement: updates?.moveMidPointsWithElement,
|
||||
});
|
||||
LinearElementEditor.movePoints(
|
||||
element,
|
||||
app.scene,
|
||||
positions,
|
||||
{
|
||||
startBinding: updates?.startBinding,
|
||||
endBinding: updates?.endBinding,
|
||||
moveMidPointsWithElement: updates?.moveMidPointsWithElement,
|
||||
},
|
||||
{
|
||||
isBindingEnabled: app.state.isBindingEnabled,
|
||||
isMidpointSnappingEnabled: app.state.isMidpointSnappingEnabled,
|
||||
},
|
||||
);
|
||||
// Set the suggested binding from the updates if available
|
||||
if (isBindingElement(element, false)) {
|
||||
if (isBindingEnabled(app.state)) {
|
||||
@@ -418,6 +427,7 @@ export class LinearElementEditor {
|
||||
"start",
|
||||
elementsMap,
|
||||
app.state.zoom,
|
||||
app.state.isMidpointSnappingEnabled,
|
||||
)
|
||||
: linearElementEditor.initialState.altFocusPoint,
|
||||
},
|
||||
@@ -538,11 +548,20 @@ export class LinearElementEditor {
|
||||
linearElementEditor,
|
||||
);
|
||||
|
||||
LinearElementEditor.movePoints(element, app.scene, positions, {
|
||||
startBinding: updates?.startBinding,
|
||||
endBinding: updates?.endBinding,
|
||||
moveMidPointsWithElement: updates?.moveMidPointsWithElement,
|
||||
});
|
||||
LinearElementEditor.movePoints(
|
||||
element,
|
||||
app.scene,
|
||||
positions,
|
||||
{
|
||||
startBinding: updates?.startBinding,
|
||||
endBinding: updates?.endBinding,
|
||||
moveMidPointsWithElement: updates?.moveMidPointsWithElement,
|
||||
},
|
||||
{
|
||||
isBindingEnabled: app.state.isBindingEnabled,
|
||||
isMidpointSnappingEnabled: app.state.isMidpointSnappingEnabled,
|
||||
},
|
||||
);
|
||||
|
||||
// Set the suggested binding from the updates if available
|
||||
if (isBindingElement(element, false)) {
|
||||
@@ -636,6 +655,7 @@ export class LinearElementEditor {
|
||||
"start",
|
||||
elementsMap,
|
||||
app.state.zoom,
|
||||
app.state.isMidpointSnappingEnabled,
|
||||
)
|
||||
: linearElementEditor.initialState.altFocusPoint,
|
||||
},
|
||||
@@ -1524,6 +1544,10 @@ export class LinearElementEditor {
|
||||
endBinding?: FixedPointBinding | null;
|
||||
moveMidPointsWithElement?: boolean | null;
|
||||
},
|
||||
options?: {
|
||||
isBindingEnabled?: boolean;
|
||||
isMidpointSnappingEnabled?: boolean;
|
||||
},
|
||||
) {
|
||||
const { points } = element;
|
||||
|
||||
@@ -1592,6 +1616,8 @@ export class LinearElementEditor {
|
||||
otherUpdates,
|
||||
{
|
||||
isDragging: Array.from(pointUpdates.values()).some((t) => t.isDragging),
|
||||
isBindingEnabled: options?.isBindingEnabled,
|
||||
isMidpointSnappingEnabled: options?.isMidpointSnappingEnabled,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1706,6 +1732,8 @@ export class LinearElementEditor {
|
||||
isDragging?: boolean;
|
||||
zoom?: AppState["zoom"];
|
||||
sceneElementsMap?: NonDeletedSceneElementsMap;
|
||||
isBindingEnabled?: boolean;
|
||||
isMidpointSnappingEnabled?: boolean;
|
||||
},
|
||||
) {
|
||||
if (isElbowArrow(element)) {
|
||||
@@ -1726,6 +1754,8 @@ export class LinearElementEditor {
|
||||
scene.mutateElement(element, updates, {
|
||||
informMutation: true,
|
||||
isDragging: options?.isDragging ?? false,
|
||||
isBindingEnabled: options?.isBindingEnabled,
|
||||
isMidpointSnappingEnabled: options?.isMidpointSnappingEnabled,
|
||||
});
|
||||
} else {
|
||||
// TODO do we need to get precise coords here just to calc centers?
|
||||
@@ -2145,14 +2175,16 @@ const pointDraggingUpdates = (
|
||||
suggestedBinding: suggestedBindingElement
|
||||
? {
|
||||
element: suggestedBindingElement,
|
||||
midPoint: snapToMid(
|
||||
suggestedBindingElement,
|
||||
elementsMap,
|
||||
pointFrom<GlobalPoint>(
|
||||
scenePointerX - linearElementEditor.pointerOffset.x,
|
||||
scenePointerY - linearElementEditor.pointerOffset.y,
|
||||
),
|
||||
),
|
||||
midPoint: app.state.isMidpointSnappingEnabled
|
||||
? snapToMid(
|
||||
suggestedBindingElement,
|
||||
elementsMap,
|
||||
pointFrom<GlobalPoint>(
|
||||
scenePointerX - linearElementEditor.pointerOffset.x,
|
||||
scenePointerY - linearElementEditor.pointerOffset.y,
|
||||
),
|
||||
)
|
||||
: undefined,
|
||||
}
|
||||
: null,
|
||||
},
|
||||
|
||||
@@ -40,6 +40,8 @@ export const mutateElement = <TElement extends Mutable<ExcalidrawElement>>(
|
||||
updates: ElementUpdate<TElement>,
|
||||
options?: {
|
||||
isDragging?: boolean;
|
||||
isBindingEnabled?: boolean;
|
||||
isMidpointSnappingEnabled?: boolean;
|
||||
},
|
||||
) => {
|
||||
let didChange = false;
|
||||
|
||||
@@ -15,7 +15,7 @@ import type {
|
||||
ValueOf,
|
||||
} from "@excalidraw/common/utility-types";
|
||||
|
||||
export type ChartType = "bar" | "line";
|
||||
export type ChartType = "bar" | "line" | "radar";
|
||||
export type FillStyle = "hachure" | "cross-hatch" | "solid" | "zigzag";
|
||||
export type FontFamilyKeys = keyof typeof FONT_FAMILY;
|
||||
export type FontFamilyValues = typeof FONT_FAMILY[FontFamilyKeys];
|
||||
|
||||
@@ -659,20 +659,23 @@ export const projectFixedPointOntoDiagonal = (
|
||||
startOrEnd: "start" | "end",
|
||||
elementsMap: ElementsMap,
|
||||
zoom: AppState["zoom"],
|
||||
isMidpointSnappingEnabled: boolean = true,
|
||||
): GlobalPoint | null => {
|
||||
invariant(arrow.points.length >= 2, "Arrow must have at least two points");
|
||||
if (arrow.width < 3 && arrow.height < 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sideMidPoint = getSnapOutlineMidPoint(
|
||||
point,
|
||||
element,
|
||||
elementsMap,
|
||||
zoom,
|
||||
);
|
||||
if (sideMidPoint) {
|
||||
return sideMidPoint;
|
||||
if (isMidpointSnappingEnabled) {
|
||||
const sideMidPoint = getSnapOutlineMidPoint(
|
||||
point,
|
||||
element,
|
||||
elementsMap,
|
||||
zoom,
|
||||
);
|
||||
if (sideMidPoint) {
|
||||
return sideMidPoint;
|
||||
}
|
||||
}
|
||||
|
||||
// Do the projection onto the diagonals (or center lines
|
||||
|
||||
@@ -118,7 +118,6 @@ export const actionClearCanvas = register({
|
||||
gridStep: appState.gridStep,
|
||||
gridModeEnabled: appState.gridModeEnabled,
|
||||
stats: appState.stats,
|
||||
pasteDialog: appState.pasteDialog,
|
||||
activeTool:
|
||||
appState.activeTool.type === "image"
|
||||
? {
|
||||
|
||||
@@ -1830,6 +1830,7 @@ export const actionChangeArrowType = register<keyof typeof ARROW_TYPE>({
|
||||
startElement,
|
||||
"start",
|
||||
elementsMap,
|
||||
appState.isBindingEnabled,
|
||||
),
|
||||
}
|
||||
: null;
|
||||
@@ -1843,6 +1844,7 @@ export const actionChangeArrowType = register<keyof typeof ARROW_TYPE>({
|
||||
endElement,
|
||||
"end",
|
||||
elementsMap,
|
||||
appState.isBindingEnabled,
|
||||
),
|
||||
}
|
||||
: null;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { CaptureUpdateAction } from "@excalidraw/element";
|
||||
|
||||
import { register } from "./register";
|
||||
|
||||
export const actionToggleArrowBinding = register({
|
||||
name: "arrowBinding",
|
||||
label: "labels.arrowBinding",
|
||||
viewMode: false,
|
||||
trackEvent: {
|
||||
category: "canvas",
|
||||
predicate: (appState) => appState.bindingPreference === "disabled",
|
||||
},
|
||||
perform(elements, appState) {
|
||||
const newPreference =
|
||||
appState.bindingPreference === "enabled" ? "disabled" : "enabled";
|
||||
return {
|
||||
appState: {
|
||||
...appState,
|
||||
bindingPreference: newPreference,
|
||||
isBindingEnabled: newPreference === "enabled",
|
||||
},
|
||||
captureUpdate: CaptureUpdateAction.NEVER,
|
||||
};
|
||||
},
|
||||
checked: (appState) => appState.bindingPreference === "enabled",
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { CaptureUpdateAction } from "@excalidraw/element";
|
||||
|
||||
import { register } from "./register";
|
||||
|
||||
export const actionToggleMidpointSnapping = register({
|
||||
name: "midpointSnapping",
|
||||
label: "labels.midpointSnapping",
|
||||
viewMode: false,
|
||||
trackEvent: {
|
||||
category: "canvas",
|
||||
predicate: (appState) => !appState.isMidpointSnappingEnabled,
|
||||
},
|
||||
perform(elements, appState) {
|
||||
return {
|
||||
appState: {
|
||||
...appState,
|
||||
isMidpointSnappingEnabled: !this.checked!(appState),
|
||||
},
|
||||
captureUpdate: CaptureUpdateAction.NEVER,
|
||||
};
|
||||
},
|
||||
checked: (appState) => appState.isMidpointSnappingEnabled,
|
||||
});
|
||||
@@ -79,6 +79,8 @@ export {
|
||||
export { actionToggleGridMode } from "./actionToggleGridMode";
|
||||
export { actionToggleZenMode } from "./actionToggleZenMode";
|
||||
export { actionToggleObjectsSnapMode } from "./actionToggleObjectsSnapMode";
|
||||
export { actionToggleArrowBinding } from "./actionToggleArrowBinding";
|
||||
export { actionToggleMidpointSnapping } from "./actionToggleMidpointSnapping";
|
||||
|
||||
export { actionToggleStats } from "./actionToggleStats";
|
||||
export { actionUnbindText, actionBindText } from "./actionBoundText";
|
||||
|
||||
@@ -59,6 +59,8 @@ export type ActionName =
|
||||
| "gridMode"
|
||||
| "zenMode"
|
||||
| "objectsSnapMode"
|
||||
| "arrowBinding"
|
||||
| "midpointSnapping"
|
||||
| "stats"
|
||||
| "changeStrokeColor"
|
||||
| "changeBackgroundColor"
|
||||
|
||||
@@ -27,7 +27,6 @@ export const getDefaultAppState = (): Omit<
|
||||
showWelcomeScreen: false,
|
||||
theme: THEME.LIGHT,
|
||||
collaborators: new Map(),
|
||||
currentChartType: "bar",
|
||||
currentItemBackgroundColor: DEFAULT_ELEMENT_PROPS.backgroundColor,
|
||||
currentItemEndArrowhead: "arrow",
|
||||
currentItemFillStyle: DEFAULT_ELEMENT_PROPS.fillStyle,
|
||||
@@ -71,6 +70,8 @@ export const getDefaultAppState = (): Omit<
|
||||
gridStep: DEFAULT_GRID_STEP,
|
||||
gridModeEnabled: false,
|
||||
isBindingEnabled: true,
|
||||
bindingPreference: "enabled",
|
||||
isMidpointSnappingEnabled: true,
|
||||
defaultSidebarDockedPreference: false,
|
||||
isLoading: false,
|
||||
isResizing: false,
|
||||
@@ -83,7 +84,6 @@ export const getDefaultAppState = (): Omit<
|
||||
openPopup: null,
|
||||
openSidebar: null,
|
||||
openDialog: null,
|
||||
pasteDialog: { shown: false, data: null },
|
||||
previousSelectedElementIds: {},
|
||||
resizingElement: null,
|
||||
scrolledOutside: false,
|
||||
@@ -150,7 +150,6 @@ const APP_STATE_STORAGE_CONF = (<
|
||||
showWelcomeScreen: { browser: true, export: false, server: false },
|
||||
theme: { browser: true, export: false, server: false },
|
||||
collaborators: { browser: false, export: false, server: false },
|
||||
currentChartType: { browser: true, export: false, server: false },
|
||||
currentItemBackgroundColor: { browser: true, export: false, server: false },
|
||||
currentItemEndArrowhead: { browser: true, export: false, server: false },
|
||||
currentItemFillStyle: { browser: true, export: false, server: false },
|
||||
@@ -193,7 +192,9 @@ const APP_STATE_STORAGE_CONF = (<
|
||||
gridStep: { browser: true, export: true, server: true },
|
||||
gridModeEnabled: { browser: true, export: true, server: true },
|
||||
height: { browser: false, export: false, server: false },
|
||||
isBindingEnabled: { browser: false, export: false, server: false },
|
||||
isBindingEnabled: { browser: true, export: false, server: false },
|
||||
bindingPreference: { browser: true, export: false, server: false },
|
||||
isMidpointSnappingEnabled: { browser: true, export: false, server: false },
|
||||
defaultSidebarDockedPreference: {
|
||||
browser: true,
|
||||
export: false,
|
||||
@@ -212,7 +213,6 @@ const APP_STATE_STORAGE_CONF = (<
|
||||
openPopup: { browser: false, export: false, server: false },
|
||||
openSidebar: { browser: true, export: false, server: false },
|
||||
openDialog: { browser: false, export: false, server: false },
|
||||
pasteDialog: { browser: false, export: false, server: false },
|
||||
previousSelectedElementIds: { browser: true, export: false, server: false },
|
||||
resizingElement: { browser: false, export: false, server: false },
|
||||
scrolledOutside: { browser: true, export: false, server: false },
|
||||
|
||||
+1063
-16
File diff suppressed because it is too large
Load Diff
@@ -1,481 +0,0 @@
|
||||
import { pointFrom } from "@excalidraw/math";
|
||||
|
||||
import {
|
||||
COLOR_PALETTE,
|
||||
DEFAULT_CHART_COLOR_INDEX,
|
||||
getAllColorsSpecificShade,
|
||||
DEFAULT_FONT_FAMILY,
|
||||
DEFAULT_FONT_SIZE,
|
||||
VERTICAL_ALIGN,
|
||||
randomId,
|
||||
isDevEnv,
|
||||
FONT_SIZES,
|
||||
} from "@excalidraw/common";
|
||||
|
||||
import {
|
||||
newTextElement,
|
||||
newLinearElement,
|
||||
newElement,
|
||||
} from "@excalidraw/element";
|
||||
|
||||
import type { Radians } from "@excalidraw/math";
|
||||
|
||||
import type { NonDeletedExcalidrawElement } from "@excalidraw/element/types";
|
||||
|
||||
export type ChartElements = readonly NonDeletedExcalidrawElement[];
|
||||
|
||||
const BAR_WIDTH = 32;
|
||||
const BAR_GAP = 12;
|
||||
const BAR_HEIGHT = 256;
|
||||
const GRID_OPACITY = 50;
|
||||
|
||||
export interface Spreadsheet {
|
||||
title: string | null;
|
||||
labels: string[] | null;
|
||||
values: number[];
|
||||
}
|
||||
|
||||
export const NOT_SPREADSHEET = "NOT_SPREADSHEET";
|
||||
export const VALID_SPREADSHEET = "VALID_SPREADSHEET";
|
||||
|
||||
type ParseSpreadsheetResult =
|
||||
| { type: typeof NOT_SPREADSHEET; reason: string }
|
||||
| { type: typeof VALID_SPREADSHEET; spreadsheet: Spreadsheet };
|
||||
|
||||
/**
|
||||
* @private exported for testing
|
||||
*/
|
||||
export const tryParseNumber = (s: string): number | null => {
|
||||
const match = /^([-+]?)[$€£¥₩]?([-+]?)([\d.,]+)[%]?$/.exec(s);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
return parseFloat(`${(match[1] || match[2]) + match[3]}`.replace(/,/g, ""));
|
||||
};
|
||||
|
||||
const isNumericColumn = (lines: string[][], columnIndex: number) =>
|
||||
lines.slice(1).every((line) => tryParseNumber(line[columnIndex]) !== null);
|
||||
|
||||
/**
|
||||
* @private exported for testing
|
||||
*/
|
||||
export const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
|
||||
const numCols = cells[0].length;
|
||||
|
||||
if (numCols > 2) {
|
||||
return { type: NOT_SPREADSHEET, reason: "More than 2 columns" };
|
||||
}
|
||||
|
||||
if (numCols === 1) {
|
||||
if (!isNumericColumn(cells, 0)) {
|
||||
return { type: NOT_SPREADSHEET, reason: "Value is not numeric" };
|
||||
}
|
||||
|
||||
const hasHeader = tryParseNumber(cells[0][0]) === null;
|
||||
const values = (hasHeader ? cells.slice(1) : cells).map((line) =>
|
||||
tryParseNumber(line[0]),
|
||||
);
|
||||
|
||||
if (values.length < 2) {
|
||||
return { type: NOT_SPREADSHEET, reason: "Less than two rows" };
|
||||
}
|
||||
|
||||
return {
|
||||
type: VALID_SPREADSHEET,
|
||||
spreadsheet: {
|
||||
title: hasHeader ? cells[0][0] : null,
|
||||
labels: null,
|
||||
values: values as number[],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const labelColumnNumeric = isNumericColumn(cells, 0);
|
||||
const valueColumnNumeric = isNumericColumn(cells, 1);
|
||||
|
||||
if (!labelColumnNumeric && !valueColumnNumeric) {
|
||||
return { type: NOT_SPREADSHEET, reason: "Value is not numeric" };
|
||||
}
|
||||
|
||||
const [labelColumnIndex, valueColumnIndex] = valueColumnNumeric
|
||||
? [0, 1]
|
||||
: [1, 0];
|
||||
const hasHeader = tryParseNumber(cells[0][valueColumnIndex]) === null;
|
||||
const rows = hasHeader ? cells.slice(1) : cells;
|
||||
|
||||
if (rows.length < 2) {
|
||||
return { type: NOT_SPREADSHEET, reason: "Less than 2 rows" };
|
||||
}
|
||||
|
||||
return {
|
||||
type: VALID_SPREADSHEET,
|
||||
spreadsheet: {
|
||||
title: hasHeader ? cells[0][valueColumnIndex] : null,
|
||||
labels: rows.map((row) => row[labelColumnIndex]),
|
||||
values: rows.map((row) => tryParseNumber(row[valueColumnIndex])!),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const transposeCells = (cells: string[][]) => {
|
||||
const nextCells: string[][] = [];
|
||||
for (let col = 0; col < cells[0].length; col++) {
|
||||
const nextCellRow: string[] = [];
|
||||
for (let row = 0; row < cells.length; row++) {
|
||||
nextCellRow.push(cells[row][col]);
|
||||
}
|
||||
nextCells.push(nextCellRow);
|
||||
}
|
||||
return nextCells;
|
||||
};
|
||||
|
||||
export const tryParseSpreadsheet = (text: string): ParseSpreadsheetResult => {
|
||||
// Copy/paste from excel, spreadsheets, tsv, csv.
|
||||
// For now we only accept 2 columns with an optional header
|
||||
|
||||
// Check for tab separated values
|
||||
let lines = text
|
||||
.trim()
|
||||
.split("\n")
|
||||
.map((line) => line.trim().split("\t"));
|
||||
|
||||
// Check for comma separated files
|
||||
if (lines.length && lines[0].length !== 2) {
|
||||
lines = text
|
||||
.trim()
|
||||
.split("\n")
|
||||
.map((line) => line.trim().split(","));
|
||||
}
|
||||
|
||||
if (lines.length === 0) {
|
||||
return { type: NOT_SPREADSHEET, reason: "No values" };
|
||||
}
|
||||
|
||||
const numColsFirstLine = lines[0].length;
|
||||
const isSpreadsheet = lines.every((line) => line.length === numColsFirstLine);
|
||||
|
||||
if (!isSpreadsheet) {
|
||||
return {
|
||||
type: NOT_SPREADSHEET,
|
||||
reason: "All rows don't have same number of columns",
|
||||
};
|
||||
}
|
||||
|
||||
const result = tryParseCells(lines);
|
||||
if (result.type !== VALID_SPREADSHEET) {
|
||||
const transposedResults = tryParseCells(transposeCells(lines));
|
||||
if (transposedResults.type === VALID_SPREADSHEET) {
|
||||
return transposedResults;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const bgColors = getAllColorsSpecificShade(DEFAULT_CHART_COLOR_INDEX);
|
||||
|
||||
// Put all the common properties here so when the whole chart is selected
|
||||
// the properties dialog shows the correct selected values
|
||||
const commonProps = {
|
||||
fillStyle: "hachure",
|
||||
fontFamily: DEFAULT_FONT_FAMILY,
|
||||
fontSize: DEFAULT_FONT_SIZE,
|
||||
opacity: 100,
|
||||
roughness: 1,
|
||||
strokeColor: COLOR_PALETTE.black,
|
||||
roundness: null,
|
||||
strokeStyle: "solid",
|
||||
strokeWidth: 1,
|
||||
verticalAlign: VERTICAL_ALIGN.MIDDLE,
|
||||
locked: false,
|
||||
} as const;
|
||||
|
||||
const getChartDimensions = (spreadsheet: Spreadsheet) => {
|
||||
const chartWidth =
|
||||
(BAR_WIDTH + BAR_GAP) * spreadsheet.values.length + BAR_GAP;
|
||||
const chartHeight = BAR_HEIGHT + BAR_GAP * 2;
|
||||
return { chartWidth, chartHeight };
|
||||
};
|
||||
|
||||
const chartXLabels = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
groupId: string,
|
||||
backgroundColor: string,
|
||||
): ChartElements => {
|
||||
return (
|
||||
spreadsheet.labels?.map((label, index) => {
|
||||
return newTextElement({
|
||||
groupIds: [groupId],
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
text: label.length > 8 ? `${label.slice(0, 5)}...` : label,
|
||||
x: x + index * (BAR_WIDTH + BAR_GAP) + BAR_GAP * 2,
|
||||
y: y + BAR_GAP / 2,
|
||||
width: BAR_WIDTH,
|
||||
angle: 5.87 as Radians,
|
||||
fontSize: FONT_SIZES.sm,
|
||||
textAlign: "center",
|
||||
verticalAlign: "top",
|
||||
});
|
||||
}) || []
|
||||
);
|
||||
};
|
||||
|
||||
const chartYLabels = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
groupId: string,
|
||||
backgroundColor: string,
|
||||
): ChartElements => {
|
||||
const minYLabel = newTextElement({
|
||||
groupIds: [groupId],
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
x: x - BAR_GAP,
|
||||
y: y - BAR_GAP,
|
||||
text: "0",
|
||||
textAlign: "right",
|
||||
});
|
||||
|
||||
const maxYLabel = newTextElement({
|
||||
groupIds: [groupId],
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
x: x - BAR_GAP,
|
||||
y: y - BAR_HEIGHT - minYLabel.height / 2,
|
||||
text: Math.max(...spreadsheet.values).toLocaleString(),
|
||||
textAlign: "right",
|
||||
});
|
||||
|
||||
return [minYLabel, maxYLabel];
|
||||
};
|
||||
|
||||
const chartLines = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
groupId: string,
|
||||
backgroundColor: string,
|
||||
): ChartElements => {
|
||||
const { chartWidth, chartHeight } = getChartDimensions(spreadsheet);
|
||||
const xLine = newLinearElement({
|
||||
backgroundColor,
|
||||
groupIds: [groupId],
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x,
|
||||
y,
|
||||
width: chartWidth,
|
||||
points: [pointFrom(0, 0), pointFrom(chartWidth, 0)],
|
||||
});
|
||||
|
||||
const yLine = newLinearElement({
|
||||
backgroundColor,
|
||||
groupIds: [groupId],
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x,
|
||||
y,
|
||||
height: chartHeight,
|
||||
points: [pointFrom(0, 0), pointFrom(0, -chartHeight)],
|
||||
});
|
||||
|
||||
const maxLine = newLinearElement({
|
||||
backgroundColor,
|
||||
groupIds: [groupId],
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x,
|
||||
y: y - BAR_HEIGHT - BAR_GAP,
|
||||
strokeStyle: "dotted",
|
||||
width: chartWidth,
|
||||
opacity: GRID_OPACITY,
|
||||
points: [pointFrom(0, 0), pointFrom(chartWidth, 0)],
|
||||
});
|
||||
|
||||
return [xLine, yLine, maxLine];
|
||||
};
|
||||
|
||||
// For the maths behind it https://excalidraw.com/#json=6320864370884608,O_5xfD-Agh32tytHpRJx1g
|
||||
const chartBaseElements = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
groupId: string,
|
||||
backgroundColor: string,
|
||||
debug?: boolean,
|
||||
): ChartElements => {
|
||||
const { chartWidth, chartHeight } = getChartDimensions(spreadsheet);
|
||||
|
||||
const title = spreadsheet.title
|
||||
? newTextElement({
|
||||
backgroundColor,
|
||||
groupIds: [groupId],
|
||||
...commonProps,
|
||||
text: spreadsheet.title,
|
||||
x: x + chartWidth / 2,
|
||||
y: y - BAR_HEIGHT - BAR_GAP * 2 - DEFAULT_FONT_SIZE,
|
||||
roundness: null,
|
||||
textAlign: "center",
|
||||
})
|
||||
: null;
|
||||
|
||||
const debugRect = debug
|
||||
? newElement({
|
||||
backgroundColor,
|
||||
groupIds: [groupId],
|
||||
...commonProps,
|
||||
type: "rectangle",
|
||||
x,
|
||||
y: y - chartHeight,
|
||||
width: chartWidth,
|
||||
height: chartHeight,
|
||||
strokeColor: COLOR_PALETTE.black,
|
||||
fillStyle: "solid",
|
||||
opacity: 6,
|
||||
})
|
||||
: null;
|
||||
|
||||
return [
|
||||
...(debugRect ? [debugRect] : []),
|
||||
...(title ? [title] : []),
|
||||
...chartXLabels(spreadsheet, x, y, groupId, backgroundColor),
|
||||
...chartYLabels(spreadsheet, x, y, groupId, backgroundColor),
|
||||
...chartLines(spreadsheet, x, y, groupId, backgroundColor),
|
||||
];
|
||||
};
|
||||
|
||||
const chartTypeBar = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
): ChartElements => {
|
||||
const max = Math.max(...spreadsheet.values);
|
||||
const groupId = randomId();
|
||||
const backgroundColor = bgColors[Math.floor(Math.random() * bgColors.length)];
|
||||
|
||||
const bars = spreadsheet.values.map((value, index) => {
|
||||
const barHeight = (value / max) * BAR_HEIGHT;
|
||||
return newElement({
|
||||
backgroundColor,
|
||||
groupIds: [groupId],
|
||||
...commonProps,
|
||||
type: "rectangle",
|
||||
x: x + index * (BAR_WIDTH + BAR_GAP) + BAR_GAP,
|
||||
y: y - barHeight - BAR_GAP,
|
||||
width: BAR_WIDTH,
|
||||
height: barHeight,
|
||||
});
|
||||
});
|
||||
|
||||
return [
|
||||
...bars,
|
||||
...chartBaseElements(
|
||||
spreadsheet,
|
||||
x,
|
||||
y,
|
||||
groupId,
|
||||
backgroundColor,
|
||||
isDevEnv(),
|
||||
),
|
||||
];
|
||||
};
|
||||
|
||||
const chartTypeLine = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
): ChartElements => {
|
||||
const max = Math.max(...spreadsheet.values);
|
||||
const groupId = randomId();
|
||||
const backgroundColor = bgColors[Math.floor(Math.random() * bgColors.length)];
|
||||
|
||||
let index = 0;
|
||||
const points = [];
|
||||
for (const value of spreadsheet.values) {
|
||||
const cx = index * (BAR_WIDTH + BAR_GAP);
|
||||
const cy = -(value / max) * BAR_HEIGHT;
|
||||
points.push([cx, cy]);
|
||||
index++;
|
||||
}
|
||||
|
||||
const maxX = Math.max(...points.map((element) => element[0]));
|
||||
const maxY = Math.max(...points.map((element) => element[1]));
|
||||
const minX = Math.min(...points.map((element) => element[0]));
|
||||
const minY = Math.min(...points.map((element) => element[1]));
|
||||
|
||||
const line = newLinearElement({
|
||||
backgroundColor,
|
||||
groupIds: [groupId],
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x: x + BAR_GAP + BAR_WIDTH / 2,
|
||||
y: y - BAR_GAP,
|
||||
height: maxY - minY,
|
||||
width: maxX - minX,
|
||||
strokeWidth: 2,
|
||||
points: points as any,
|
||||
});
|
||||
|
||||
const dots = spreadsheet.values.map((value, index) => {
|
||||
const cx = index * (BAR_WIDTH + BAR_GAP) + BAR_GAP / 2;
|
||||
const cy = -(value / max) * BAR_HEIGHT + BAR_GAP / 2;
|
||||
return newElement({
|
||||
backgroundColor,
|
||||
groupIds: [groupId],
|
||||
...commonProps,
|
||||
fillStyle: "solid",
|
||||
strokeWidth: 2,
|
||||
type: "ellipse",
|
||||
x: x + cx + BAR_WIDTH / 2,
|
||||
y: y + cy - BAR_GAP * 2,
|
||||
width: BAR_GAP,
|
||||
height: BAR_GAP,
|
||||
});
|
||||
});
|
||||
|
||||
const lines = spreadsheet.values.map((value, index) => {
|
||||
const cx = index * (BAR_WIDTH + BAR_GAP) + BAR_GAP / 2;
|
||||
const cy = (value / max) * BAR_HEIGHT + BAR_GAP / 2 + BAR_GAP;
|
||||
return newLinearElement({
|
||||
backgroundColor,
|
||||
groupIds: [groupId],
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x: x + cx + BAR_WIDTH / 2 + BAR_GAP / 2,
|
||||
y: y - cy,
|
||||
height: cy,
|
||||
strokeStyle: "dotted",
|
||||
opacity: GRID_OPACITY,
|
||||
points: [pointFrom(0, 0), pointFrom(0, cy)],
|
||||
});
|
||||
});
|
||||
|
||||
return [
|
||||
...chartBaseElements(
|
||||
spreadsheet,
|
||||
x,
|
||||
y,
|
||||
groupId,
|
||||
backgroundColor,
|
||||
isDevEnv(),
|
||||
),
|
||||
line,
|
||||
...lines,
|
||||
...dots,
|
||||
];
|
||||
};
|
||||
|
||||
export const renderSpreadsheet = (
|
||||
chartType: string,
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
): ChartElements => {
|
||||
if (chartType === "line") {
|
||||
return chartTypeLine(spreadsheet, x, y);
|
||||
}
|
||||
return chartTypeBar(spreadsheet, x, y);
|
||||
};
|
||||
@@ -0,0 +1,103 @@
|
||||
import { isDevEnv } from "@excalidraw/common";
|
||||
|
||||
import { newElement } from "@excalidraw/element";
|
||||
|
||||
import { commonProps } from "./charts.constants";
|
||||
import {
|
||||
chartBaseElements,
|
||||
chartXLabels,
|
||||
createSeriesLegend,
|
||||
getBackgroundColor,
|
||||
getCartesianChartLayout,
|
||||
getChartDimensions,
|
||||
getColorOffset,
|
||||
getRotatedTextElementBottom,
|
||||
getSeriesColors,
|
||||
} from "./charts.helpers";
|
||||
|
||||
import type { ChartElements, Spreadsheet } from "./charts.types";
|
||||
|
||||
export const renderBarChart = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
colorSeed?: number,
|
||||
): ChartElements => {
|
||||
const series = spreadsheet.series;
|
||||
const layout = getCartesianChartLayout("bar", series.length);
|
||||
const max = Math.max(
|
||||
1,
|
||||
...series.flatMap((seriesData) =>
|
||||
seriesData.values.map((value) => Math.max(0, value)),
|
||||
),
|
||||
);
|
||||
const colorOffset = getColorOffset(colorSeed);
|
||||
const backgroundColor = getBackgroundColor(colorOffset);
|
||||
const seriesColors = getSeriesColors(series.length, colorOffset);
|
||||
const interBarGap =
|
||||
series.length > 1
|
||||
? Math.max(1, Math.floor(layout.gap / (series.length + 1)))
|
||||
: 0;
|
||||
const barWidth =
|
||||
series.length > 1
|
||||
? Math.max(
|
||||
2,
|
||||
(layout.slotWidth - interBarGap * (series.length - 1)) /
|
||||
series.length,
|
||||
)
|
||||
: layout.slotWidth;
|
||||
const clusterWidth =
|
||||
series.length * barWidth + interBarGap * (series.length - 1);
|
||||
const clusterOffset = (layout.slotWidth - clusterWidth) / 2;
|
||||
|
||||
const bars = series[0].values.flatMap((_, categoryIndex) =>
|
||||
series.map((seriesData, seriesIndex) => {
|
||||
const value = Math.max(0, seriesData.values[categoryIndex] ?? 0);
|
||||
const barHeight = (value / max) * layout.chartHeight;
|
||||
const barColor =
|
||||
series.length > 1 ? seriesColors[seriesIndex] : backgroundColor;
|
||||
return newElement({
|
||||
backgroundColor: barColor,
|
||||
...commonProps,
|
||||
type: "rectangle",
|
||||
fillStyle: series.length > 1 ? "solid" : commonProps.fillStyle,
|
||||
strokeColor: series.length > 1 ? barColor : commonProps.strokeColor,
|
||||
x:
|
||||
x +
|
||||
categoryIndex * (layout.slotWidth + layout.gap) +
|
||||
layout.gap +
|
||||
clusterOffset +
|
||||
seriesIndex * (barWidth + interBarGap),
|
||||
y: y - barHeight - layout.gap,
|
||||
width: barWidth,
|
||||
height: barHeight,
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
const baseElements = chartBaseElements(
|
||||
spreadsheet,
|
||||
x,
|
||||
y,
|
||||
backgroundColor,
|
||||
layout,
|
||||
max,
|
||||
isDevEnv(),
|
||||
);
|
||||
const xLabels = chartXLabels(spreadsheet, x, y, backgroundColor, layout);
|
||||
const xLabelsBottomY = Math.max(
|
||||
y + layout.gap / 2,
|
||||
...xLabels.map((label) => getRotatedTextElementBottom(label)),
|
||||
);
|
||||
const { chartWidth } = getChartDimensions(spreadsheet, layout);
|
||||
const seriesLegend = createSeriesLegend(
|
||||
series,
|
||||
seriesColors,
|
||||
x + chartWidth / 2,
|
||||
xLabelsBottomY,
|
||||
y + layout.gap * 5,
|
||||
backgroundColor,
|
||||
);
|
||||
|
||||
return [...baseElements, ...bars, ...seriesLegend];
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
import {
|
||||
COLOR_PALETTE,
|
||||
DEFAULT_FONT_FAMILY,
|
||||
DEFAULT_FONT_SIZE,
|
||||
VERTICAL_ALIGN,
|
||||
} from "@excalidraw/common";
|
||||
|
||||
import type { Radians } from "@excalidraw/math";
|
||||
|
||||
export const CARTESIAN_BASE_SLOT_WIDTH = 44;
|
||||
export const CARTESIAN_BAR_SLOT_EXTRA_PER_SERIES = 22;
|
||||
export const CARTESIAN_BAR_SLOT_EXTRA_MAX = 66;
|
||||
export const CARTESIAN_LINE_SLOT_WIDTH = 48;
|
||||
export const CARTESIAN_GAP = 14;
|
||||
export const CARTESIAN_BAR_HEIGHT = 304;
|
||||
export const CARTESIAN_LINE_HEIGHT = 320;
|
||||
export const CARTESIAN_LABEL_ROTATION = 5.87 as Radians;
|
||||
export const CARTESIAN_LABEL_MIN_WIDTH = 28;
|
||||
export const CARTESIAN_LABEL_SLOT_PADDING = 4;
|
||||
export const CARTESIAN_LABEL_AXIS_CLEARANCE = 2;
|
||||
export const CARTESIAN_LABEL_MAX_WIDTH_BUFFER = 10;
|
||||
export const CARTESIAN_LABEL_ROTATED_WIDTH_BUFFER = 10;
|
||||
export const CARTESIAN_LABEL_OVERFLOW_PREFERENCE_BUFFER = 8;
|
||||
|
||||
export const BAR_GAP = 12;
|
||||
export const BAR_HEIGHT = 256;
|
||||
export const GRID_OPACITY = 10;
|
||||
|
||||
export const RADAR_GRID_LEVELS = 4;
|
||||
export const RADAR_LABEL_OFFSET = BAR_GAP * 2;
|
||||
export const RADAR_PADDING = BAR_GAP * 2;
|
||||
export const RADAR_SINGLE_SERIES_LOG_SCALE_THRESHOLD = 100;
|
||||
export const RADAR_AXIS_LABEL_MAX_WIDTH = 140;
|
||||
export const RADAR_AXIS_LABEL_ALIGNMENT_THRESHOLD = 0.35;
|
||||
export const RADAR_AXIS_LABEL_CLEARANCE = BAR_GAP / 2;
|
||||
export const RADAR_LEGEND_SWATCH_SIZE = 20;
|
||||
export const RADAR_LEGEND_ITEM_GAP = BAR_GAP * 2;
|
||||
export const RADAR_LEGEND_TEXT_GAP = BAR_GAP;
|
||||
|
||||
// Put all common chart element properties here so properties dialog
|
||||
// shows stable values when selecting chart groups.
|
||||
export const commonProps = {
|
||||
fillStyle: "hachure",
|
||||
fontFamily: DEFAULT_FONT_FAMILY,
|
||||
fontSize: DEFAULT_FONT_SIZE,
|
||||
opacity: 100,
|
||||
roughness: 1,
|
||||
strokeColor: COLOR_PALETTE.black,
|
||||
roundness: null,
|
||||
strokeStyle: "solid",
|
||||
strokeWidth: 1,
|
||||
verticalAlign: VERTICAL_ALIGN.MIDDLE,
|
||||
locked: false,
|
||||
} as const;
|
||||
|
||||
export type CartesianChartType = "bar" | "line";
|
||||
|
||||
export type CartesianChartLayout = {
|
||||
slotWidth: number;
|
||||
gap: number;
|
||||
chartHeight: number;
|
||||
xLabelMaxWidth: number;
|
||||
};
|
||||
@@ -0,0 +1,865 @@
|
||||
import { pointFrom } from "@excalidraw/math";
|
||||
|
||||
import {
|
||||
COLOR_PALETTE,
|
||||
DEFAULT_CHART_COLOR_INDEX,
|
||||
FONT_FAMILY,
|
||||
FONT_SIZES,
|
||||
ROUNDNESS,
|
||||
DEFAULT_FONT_SIZE,
|
||||
getAllColorsSpecificShade,
|
||||
getFontString,
|
||||
getLineHeight,
|
||||
ROUGHNESS,
|
||||
} from "@excalidraw/common";
|
||||
|
||||
import {
|
||||
getApproxMinLineWidth,
|
||||
measureText,
|
||||
newElement,
|
||||
newLinearElement,
|
||||
newTextElement,
|
||||
wrapText,
|
||||
} from "@excalidraw/element";
|
||||
|
||||
import type {
|
||||
ChartType,
|
||||
ExcalidrawTextElement,
|
||||
} from "@excalidraw/element/types";
|
||||
import type { NonDeletedExcalidrawElement } from "@excalidraw/element/types";
|
||||
|
||||
import {
|
||||
BAR_GAP,
|
||||
CARTESIAN_BAR_HEIGHT,
|
||||
CARTESIAN_BASE_SLOT_WIDTH,
|
||||
CARTESIAN_BAR_SLOT_EXTRA_MAX,
|
||||
CARTESIAN_BAR_SLOT_EXTRA_PER_SERIES,
|
||||
CARTESIAN_GAP,
|
||||
CARTESIAN_LABEL_AXIS_CLEARANCE,
|
||||
CARTESIAN_LABEL_MAX_WIDTH_BUFFER,
|
||||
CARTESIAN_LABEL_MIN_WIDTH,
|
||||
CARTESIAN_LABEL_OVERFLOW_PREFERENCE_BUFFER,
|
||||
CARTESIAN_LABEL_ROTATED_WIDTH_BUFFER,
|
||||
CARTESIAN_LABEL_ROTATION,
|
||||
CARTESIAN_LABEL_SLOT_PADDING,
|
||||
CARTESIAN_LINE_HEIGHT,
|
||||
CARTESIAN_LINE_SLOT_WIDTH,
|
||||
GRID_OPACITY,
|
||||
RADAR_AXIS_LABEL_ALIGNMENT_THRESHOLD,
|
||||
RADAR_AXIS_LABEL_CLEARANCE,
|
||||
RADAR_AXIS_LABEL_MAX_WIDTH,
|
||||
RADAR_LABEL_OFFSET,
|
||||
RADAR_LEGEND_ITEM_GAP,
|
||||
RADAR_LEGEND_SWATCH_SIZE,
|
||||
RADAR_LEGEND_TEXT_GAP,
|
||||
RADAR_PADDING,
|
||||
RADAR_SINGLE_SERIES_LOG_SCALE_THRESHOLD,
|
||||
BAR_HEIGHT,
|
||||
commonProps,
|
||||
type CartesianChartLayout,
|
||||
type CartesianChartType,
|
||||
} from "./charts.constants";
|
||||
|
||||
import type {
|
||||
ChartElements,
|
||||
Spreadsheet,
|
||||
SpreadsheetSeries,
|
||||
} from "./charts.types";
|
||||
|
||||
const bgColors = getAllColorsSpecificShade(DEFAULT_CHART_COLOR_INDEX);
|
||||
|
||||
const getSpreadsheetDimensionCount = (spreadsheet: Spreadsheet) =>
|
||||
spreadsheet.labels?.length ?? spreadsheet.series[0]?.values.length ?? 0;
|
||||
|
||||
export const isSpreadsheetValidForChartType = (
|
||||
spreadsheet: Spreadsheet | null,
|
||||
chartType: ChartType,
|
||||
) => {
|
||||
if (!spreadsheet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const dimensionCount = getSpreadsheetDimensionCount(spreadsheet);
|
||||
if (dimensionCount < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (chartType === "radar") {
|
||||
return dimensionCount >= 3;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
const getSeriesAwareSlotWidth = (
|
||||
baseSlotWidth: number,
|
||||
seriesCount: number,
|
||||
) => {
|
||||
const extraSlotWidth =
|
||||
seriesCount <= 1
|
||||
? 0
|
||||
: Math.min(
|
||||
CARTESIAN_BAR_SLOT_EXTRA_MAX,
|
||||
(seriesCount - 1) * CARTESIAN_BAR_SLOT_EXTRA_PER_SERIES,
|
||||
);
|
||||
return baseSlotWidth + extraSlotWidth;
|
||||
};
|
||||
|
||||
export const getCartesianChartLayout = (
|
||||
chartType: CartesianChartType,
|
||||
seriesCount: number,
|
||||
): CartesianChartLayout => {
|
||||
if (chartType === "line") {
|
||||
const slotWidth = getSeriesAwareSlotWidth(
|
||||
CARTESIAN_LINE_SLOT_WIDTH,
|
||||
seriesCount,
|
||||
);
|
||||
return {
|
||||
slotWidth,
|
||||
gap: CARTESIAN_GAP,
|
||||
chartHeight: CARTESIAN_LINE_HEIGHT,
|
||||
xLabelMaxWidth:
|
||||
slotWidth + CARTESIAN_GAP * 3 + CARTESIAN_LABEL_MAX_WIDTH_BUFFER,
|
||||
};
|
||||
}
|
||||
|
||||
const slotWidth = getSeriesAwareSlotWidth(
|
||||
CARTESIAN_BASE_SLOT_WIDTH,
|
||||
seriesCount,
|
||||
);
|
||||
return {
|
||||
slotWidth,
|
||||
gap: CARTESIAN_GAP,
|
||||
chartHeight: CARTESIAN_BAR_HEIGHT,
|
||||
xLabelMaxWidth:
|
||||
slotWidth + CARTESIAN_GAP * 3 + CARTESIAN_LABEL_MAX_WIDTH_BUFFER,
|
||||
};
|
||||
};
|
||||
|
||||
export const getChartDimensions = (
|
||||
spreadsheet: Spreadsheet,
|
||||
layout: CartesianChartLayout,
|
||||
) => {
|
||||
const chartWidth =
|
||||
(layout.slotWidth + layout.gap) * spreadsheet.series[0].values.length +
|
||||
layout.gap;
|
||||
const chartHeight = layout.chartHeight + layout.gap * 2;
|
||||
return { chartWidth, chartHeight };
|
||||
};
|
||||
|
||||
export const getRadarDimensions = () => {
|
||||
const chartWidth = BAR_HEIGHT + RADAR_PADDING * 2;
|
||||
const chartHeight = BAR_HEIGHT + RADAR_PADDING * 2;
|
||||
return { chartWidth, chartHeight };
|
||||
};
|
||||
|
||||
const getCircularDistance = (
|
||||
firstIndex: number,
|
||||
secondIndex: number,
|
||||
paletteSize: number,
|
||||
) => {
|
||||
const absoluteDistance = Math.abs(firstIndex - secondIndex);
|
||||
return Math.min(absoluteDistance, paletteSize - absoluteDistance);
|
||||
};
|
||||
|
||||
export const getSeriesColors = (
|
||||
seriesCount: number,
|
||||
colorOffset: number,
|
||||
): readonly string[] => {
|
||||
if (seriesCount <= 0 || bgColors.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const paletteSize = bgColors.length;
|
||||
const startIndex = ((colorOffset % paletteSize) + paletteSize) % paletteSize;
|
||||
const selectedIndices = [startIndex];
|
||||
const maxUniqueColors = Math.min(seriesCount, paletteSize);
|
||||
const availableIndices = new Set(
|
||||
Array.from({ length: paletteSize }, (_, index) => index).filter(
|
||||
(index) => index !== startIndex,
|
||||
),
|
||||
);
|
||||
|
||||
while (selectedIndices.length < maxUniqueColors) {
|
||||
let bestIndex = -1;
|
||||
let bestMinDistance = -1;
|
||||
let bestAverageDistance = -1;
|
||||
|
||||
for (const candidateIndex of availableIndices) {
|
||||
const distances = selectedIndices.map((selectedIndex) =>
|
||||
getCircularDistance(candidateIndex, selectedIndex, paletteSize),
|
||||
);
|
||||
const minDistance = Math.min(...distances);
|
||||
const averageDistance =
|
||||
distances.reduce((total, distance) => total + distance, 0) /
|
||||
distances.length;
|
||||
|
||||
if (
|
||||
minDistance > bestMinDistance ||
|
||||
(minDistance === bestMinDistance &&
|
||||
averageDistance > bestAverageDistance)
|
||||
) {
|
||||
bestIndex = candidateIndex;
|
||||
bestMinDistance = minDistance;
|
||||
bestAverageDistance = averageDistance;
|
||||
}
|
||||
}
|
||||
|
||||
selectedIndices.push(bestIndex);
|
||||
availableIndices.delete(bestIndex);
|
||||
}
|
||||
|
||||
return Array.from(
|
||||
{ length: seriesCount },
|
||||
(_, index) => bgColors[selectedIndices[index % selectedIndices.length]],
|
||||
);
|
||||
};
|
||||
|
||||
export const getColorOffset = (colorSeed?: number) => {
|
||||
if (bgColors.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (typeof colorSeed !== "number" || !Number.isFinite(colorSeed)) {
|
||||
return Math.floor(Math.random() * bgColors.length);
|
||||
}
|
||||
|
||||
const seedText = colorSeed.toString();
|
||||
let hash = 0;
|
||||
for (let index = 0; index < seedText.length; index++) {
|
||||
hash = (hash * 31 + seedText.charCodeAt(index)) | 0;
|
||||
}
|
||||
return Math.abs(hash) % bgColors.length;
|
||||
};
|
||||
|
||||
export const getBackgroundColor = (colorOffset: number) =>
|
||||
bgColors[colorOffset];
|
||||
|
||||
export const getRadarValueScale = (
|
||||
series: SpreadsheetSeries[],
|
||||
_labelsLength: number,
|
||||
) => {
|
||||
const allValues = series.flatMap((s) =>
|
||||
s.values.map((value) => Math.max(0, value)),
|
||||
);
|
||||
const positiveValues = allValues.filter((value) => value > 0);
|
||||
const max = Math.max(1, ...allValues);
|
||||
const minPositive =
|
||||
positiveValues.length > 0 ? Math.min(...positiveValues) : 1;
|
||||
const useLogScale =
|
||||
series.length === 1 &&
|
||||
minPositive > 0 &&
|
||||
max / minPositive >= RADAR_SINGLE_SERIES_LOG_SCALE_THRESHOLD;
|
||||
|
||||
return {
|
||||
renderSteps: false,
|
||||
normalize: (value: number, _axisIndex: number) => {
|
||||
const safeValue = Math.max(0, value);
|
||||
return useLogScale
|
||||
? Math.log10(safeValue + 1) / Math.log10(max + 1)
|
||||
: safeValue / max;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const shouldWrapRadarText = (text: string) => /\s/.test(text.trim());
|
||||
|
||||
export const getRadarDisplayText = (
|
||||
text: string,
|
||||
fontString: ReturnType<typeof getFontString>,
|
||||
maxWidth: number,
|
||||
) => {
|
||||
return shouldWrapRadarText(text)
|
||||
? wrapText(text, fontString, maxWidth)
|
||||
: text;
|
||||
};
|
||||
|
||||
export const createRadarAxisLabels = (
|
||||
labels: readonly string[],
|
||||
angles: readonly number[],
|
||||
centerX: number,
|
||||
centerY: number,
|
||||
radius: number,
|
||||
backgroundColor: string,
|
||||
): {
|
||||
axisLabels: ChartElements;
|
||||
axisLabelTopY: number;
|
||||
axisLabelBottomY: number;
|
||||
} => {
|
||||
const fontFamily = FONT_FAMILY.Excalifont;
|
||||
const fontSize = FONT_SIZES.sm;
|
||||
const lineHeight = getLineHeight(fontFamily);
|
||||
const fontString = getFontString({ fontFamily, fontSize });
|
||||
const baseLabelWidth = Math.min(
|
||||
RADAR_AXIS_LABEL_MAX_WIDTH,
|
||||
radius * (labels.length > 8 ? 0.56 : 0.72),
|
||||
);
|
||||
const minLabelWidth = getApproxMinLineWidth(fontString, lineHeight);
|
||||
|
||||
const axisLabels = labels.map((label, index) => {
|
||||
const angle = angles[index];
|
||||
const longestWordWidth = Math.max(
|
||||
0,
|
||||
...label
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter(Boolean)
|
||||
.map((word) => measureText(word, fontString, lineHeight).width),
|
||||
);
|
||||
const maxLabelWidth = Math.max(
|
||||
minLabelWidth,
|
||||
baseLabelWidth,
|
||||
longestWordWidth,
|
||||
);
|
||||
const displayLabel = getRadarDisplayText(label, fontString, maxLabelWidth);
|
||||
const metrics = measureText(displayLabel, fontString, lineHeight);
|
||||
const cos = Math.cos(angle);
|
||||
const sin = Math.sin(angle);
|
||||
|
||||
const textAlign: "left" | "center" | "right" =
|
||||
cos > RADAR_AXIS_LABEL_ALIGNMENT_THRESHOLD
|
||||
? "left"
|
||||
: cos < -RADAR_AXIS_LABEL_ALIGNMENT_THRESHOLD
|
||||
? "right"
|
||||
: "center";
|
||||
|
||||
// Keep labels outside the radar ring by projecting text extents
|
||||
// onto the axis direction.
|
||||
const centerAlignedXExtent = textAlign === "center" ? metrics.width / 2 : 0;
|
||||
const projectedExtent =
|
||||
Math.abs(cos) * centerAlignedXExtent +
|
||||
Math.abs(sin) * (metrics.height / 2);
|
||||
const radialOffset =
|
||||
RADAR_LABEL_OFFSET + projectedExtent + RADAR_AXIS_LABEL_CLEARANCE;
|
||||
const anchorX = centerX + cos * (radius + radialOffset);
|
||||
const anchorY = centerY + sin * (radius + radialOffset);
|
||||
|
||||
const yNudge =
|
||||
sin > RADAR_AXIS_LABEL_ALIGNMENT_THRESHOLD
|
||||
? BAR_GAP / 3
|
||||
: sin < -RADAR_AXIS_LABEL_ALIGNMENT_THRESHOLD
|
||||
? -BAR_GAP / 3
|
||||
: 0;
|
||||
|
||||
return newTextElement({
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
text: displayLabel,
|
||||
originalText: label,
|
||||
x: anchorX,
|
||||
y: anchorY + yNudge,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
textAlign,
|
||||
verticalAlign: "middle",
|
||||
});
|
||||
});
|
||||
|
||||
const axisLabelTopY = Math.min(...axisLabels.map((axisLabel) => axisLabel.y));
|
||||
const axisLabelBottomY = Math.max(
|
||||
...axisLabels.map((axisLabel) => axisLabel.y + axisLabel.height),
|
||||
);
|
||||
return { axisLabels, axisLabelTopY, axisLabelBottomY };
|
||||
};
|
||||
|
||||
export const createSeriesLegend = (
|
||||
series: SpreadsheetSeries[],
|
||||
seriesColors: readonly string[],
|
||||
centerX: number,
|
||||
minLegendTopY: number,
|
||||
fallbackLegendY: number,
|
||||
backgroundColor: string,
|
||||
): ChartElements => {
|
||||
if (series.length <= 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const fontFamily = FONT_FAMILY["Lilita One"];
|
||||
const fontSize = FONT_SIZES.lg;
|
||||
const lineHeight = getLineHeight(fontFamily);
|
||||
const fontString = getFontString({ fontFamily, fontSize });
|
||||
const legendItems = series.map((seriesItem, index) => {
|
||||
const label = seriesItem.title?.trim() || `Series ${index + 1}`;
|
||||
const displayLabel = getRadarDisplayText(label, fontString, BAR_HEIGHT);
|
||||
const metrics = measureText(displayLabel, fontString, lineHeight);
|
||||
const itemWidth =
|
||||
RADAR_LEGEND_SWATCH_SIZE + RADAR_LEGEND_TEXT_GAP + metrics.width;
|
||||
return {
|
||||
label,
|
||||
displayLabel,
|
||||
color: seriesColors[index],
|
||||
width: itemWidth,
|
||||
height: metrics.height,
|
||||
};
|
||||
});
|
||||
const maxLegendHalfHeight = Math.max(
|
||||
RADAR_LEGEND_SWATCH_SIZE / 2,
|
||||
...legendItems.map((item) => item.height / 2),
|
||||
);
|
||||
const legendY = Math.max(
|
||||
fallbackLegendY,
|
||||
minLegendTopY + maxLegendHalfHeight + RADAR_LABEL_OFFSET,
|
||||
);
|
||||
|
||||
const pillPaddingX = RADAR_LEGEND_ITEM_GAP;
|
||||
const pillPaddingY = RADAR_LEGEND_SWATCH_SIZE * 0.6;
|
||||
const totalLegendWidth =
|
||||
legendItems.reduce((total, item) => total + item.width, 0) +
|
||||
RADAR_LEGEND_ITEM_GAP * Math.max(0, legendItems.length - 1);
|
||||
const pillWidth = totalLegendWidth + pillPaddingX * 2;
|
||||
const pillHeight = maxLegendHalfHeight * 2 + pillPaddingY * 2;
|
||||
|
||||
const legendElements: NonDeletedExcalidrawElement[] = [];
|
||||
|
||||
// rounded pill background
|
||||
legendElements.push(
|
||||
newElement({
|
||||
...commonProps,
|
||||
backgroundColor: "transparent",
|
||||
type: "rectangle",
|
||||
fillStyle: "solid",
|
||||
strokeColor: COLOR_PALETTE.black,
|
||||
x: centerX - pillWidth / 2,
|
||||
y: legendY - pillHeight / 2,
|
||||
width: pillWidth,
|
||||
height: pillHeight,
|
||||
roughness: ROUGHNESS.architect,
|
||||
roundness: { type: ROUNDNESS.PROPORTIONAL_RADIUS },
|
||||
}),
|
||||
);
|
||||
|
||||
let cursorX = centerX - totalLegendWidth / 2;
|
||||
|
||||
legendItems.forEach((item) => {
|
||||
// solid filled swatch
|
||||
legendElements.push(
|
||||
newElement({
|
||||
...commonProps,
|
||||
backgroundColor: item.color,
|
||||
type: "rectangle",
|
||||
x: cursorX,
|
||||
y: legendY - RADAR_LEGEND_SWATCH_SIZE / 2,
|
||||
width: RADAR_LEGEND_SWATCH_SIZE,
|
||||
height: RADAR_LEGEND_SWATCH_SIZE,
|
||||
fillStyle: "solid",
|
||||
strokeColor: item.color,
|
||||
roughness: ROUGHNESS.architect,
|
||||
roundness: { type: ROUNDNESS.PROPORTIONAL_RADIUS },
|
||||
}),
|
||||
);
|
||||
|
||||
// label in default (black) color
|
||||
legendElements.push(
|
||||
newTextElement({
|
||||
...commonProps,
|
||||
text: item.displayLabel,
|
||||
originalText: item.label,
|
||||
autoResize: false,
|
||||
x: cursorX + RADAR_LEGEND_SWATCH_SIZE + RADAR_LEGEND_TEXT_GAP,
|
||||
y: legendY,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
textAlign: "left",
|
||||
verticalAlign: "middle",
|
||||
}),
|
||||
);
|
||||
|
||||
cursorX += item.width + RADAR_LEGEND_ITEM_GAP;
|
||||
});
|
||||
|
||||
return legendElements;
|
||||
};
|
||||
|
||||
const ellipsifyTextToWidth = (
|
||||
text: string,
|
||||
maxWidth: number,
|
||||
fontString: ReturnType<typeof getFontString>,
|
||||
lineHeight: ExcalidrawTextElement["lineHeight"],
|
||||
) => {
|
||||
if (measureText(text, fontString, lineHeight).width <= maxWidth) {
|
||||
return text;
|
||||
}
|
||||
|
||||
let end = text.length;
|
||||
while (end > 1) {
|
||||
const candidate = `${text.slice(0, end)}...`;
|
||||
if (measureText(candidate, fontString, lineHeight).width <= maxWidth) {
|
||||
return candidate;
|
||||
}
|
||||
end--;
|
||||
}
|
||||
|
||||
return text[0] ? `${text[0]}...` : text;
|
||||
};
|
||||
|
||||
const wrapOrEllipsifyTextToWidth = (
|
||||
text: string,
|
||||
maxWidth: number,
|
||||
fontString: ReturnType<typeof getFontString>,
|
||||
lineHeight: ExcalidrawTextElement["lineHeight"],
|
||||
) => {
|
||||
if (measureText(text, fontString, lineHeight).width <= maxWidth) {
|
||||
return { wrapped: false, text };
|
||||
}
|
||||
|
||||
const words = text.trim().split(/\s+/).filter(Boolean);
|
||||
if (words.length > 1) {
|
||||
const hasLongWord = words.some((word) => {
|
||||
return measureText(word, fontString, lineHeight).width > maxWidth;
|
||||
});
|
||||
if (
|
||||
!hasLongWord &&
|
||||
maxWidth >= getApproxMinLineWidth(fontString, lineHeight)
|
||||
) {
|
||||
return { wrapped: true, text: wrapText(text, fontString, maxWidth) };
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
wrapped: false,
|
||||
text: ellipsifyTextToWidth(text, maxWidth, fontString, lineHeight),
|
||||
};
|
||||
};
|
||||
|
||||
const getRotatedBoundingBox = (
|
||||
width: number,
|
||||
height: number,
|
||||
angle: number,
|
||||
) => {
|
||||
const cos = Math.abs(Math.cos(angle));
|
||||
const sin = Math.abs(Math.sin(angle));
|
||||
return {
|
||||
width: width * cos + height * sin,
|
||||
height: width * sin + height * cos,
|
||||
};
|
||||
};
|
||||
|
||||
type CartesianAxisLabelSpec = {
|
||||
originalText: string;
|
||||
text: string;
|
||||
wrapped: boolean;
|
||||
metrics: ReturnType<typeof measureText>;
|
||||
rotatedWidth: number;
|
||||
rotatedHeight: number;
|
||||
};
|
||||
|
||||
const isEllipsifiedLabel = (text: string) => text.includes("...");
|
||||
|
||||
const getCartesianAxisLabelSpec = (
|
||||
label: string,
|
||||
maxLabelWidth: number,
|
||||
maxRotatedWidth: number,
|
||||
fontString: ReturnType<typeof getFontString>,
|
||||
lineHeight: ExcalidrawTextElement["lineHeight"],
|
||||
): CartesianAxisLabelSpec => {
|
||||
const minWidth = Math.max(
|
||||
CARTESIAN_LABEL_MIN_WIDTH,
|
||||
Math.ceil(getApproxMinLineWidth(fontString, lineHeight)),
|
||||
);
|
||||
const maxWidth = Math.max(minWidth, Math.floor(maxLabelWidth));
|
||||
const candidateWidths: number[] = [];
|
||||
for (let width = maxWidth; width >= minWidth; width -= 4) {
|
||||
candidateWidths.push(width);
|
||||
}
|
||||
if (candidateWidths[candidateWidths.length - 1] !== minWidth) {
|
||||
candidateWidths.push(minWidth);
|
||||
}
|
||||
|
||||
const getRank = (spec: CartesianAxisLabelSpec) => {
|
||||
const ellipsified = isEllipsifiedLabel(spec.text);
|
||||
const visibleChars = spec.text
|
||||
.replace(/\.\.\./g, "")
|
||||
.replace(/\n/g, "").length;
|
||||
const lineCount = spec.text.split("\n").length;
|
||||
return {
|
||||
ellipsified,
|
||||
visibleChars,
|
||||
lineCount,
|
||||
};
|
||||
};
|
||||
|
||||
const shouldPrefer = (
|
||||
candidate: CartesianAxisLabelSpec,
|
||||
current: CartesianAxisLabelSpec,
|
||||
) => {
|
||||
const candidateRank = getRank(candidate);
|
||||
const currentRank = getRank(current);
|
||||
if (candidateRank.ellipsified !== currentRank.ellipsified) {
|
||||
return !candidateRank.ellipsified;
|
||||
}
|
||||
if (candidateRank.visibleChars !== currentRank.visibleChars) {
|
||||
return candidateRank.visibleChars > currentRank.visibleChars;
|
||||
}
|
||||
if (candidateRank.lineCount !== currentRank.lineCount) {
|
||||
return candidateRank.lineCount < currentRank.lineCount;
|
||||
}
|
||||
return candidate.rotatedHeight < current.rotatedHeight;
|
||||
};
|
||||
|
||||
let bestFit: CartesianAxisLabelSpec | null = null;
|
||||
let bestOverflowAny: {
|
||||
overflow: number;
|
||||
spec: CartesianAxisLabelSpec;
|
||||
} | null = null;
|
||||
let bestOverflowNonEllipsified: {
|
||||
overflow: number;
|
||||
spec: CartesianAxisLabelSpec;
|
||||
} | null = null;
|
||||
|
||||
for (const width of candidateWidths) {
|
||||
const { wrapped, text } = wrapOrEllipsifyTextToWidth(
|
||||
label,
|
||||
width,
|
||||
fontString,
|
||||
lineHeight,
|
||||
);
|
||||
const metrics = measureText(text, fontString, lineHeight);
|
||||
const rotated = getRotatedBoundingBox(
|
||||
metrics.width,
|
||||
metrics.height,
|
||||
CARTESIAN_LABEL_ROTATION,
|
||||
);
|
||||
const spec = {
|
||||
originalText: label,
|
||||
text,
|
||||
metrics,
|
||||
rotatedWidth: rotated.width,
|
||||
rotatedHeight: rotated.height,
|
||||
wrapped,
|
||||
};
|
||||
const overflow = rotated.width - maxRotatedWidth;
|
||||
if (overflow <= 0) {
|
||||
if (!bestFit || shouldPrefer(spec, bestFit)) {
|
||||
bestFit = spec;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
!bestOverflowAny ||
|
||||
overflow < bestOverflowAny.overflow ||
|
||||
(overflow === bestOverflowAny.overflow &&
|
||||
shouldPrefer(spec, bestOverflowAny.spec))
|
||||
) {
|
||||
bestOverflowAny = { overflow, spec };
|
||||
}
|
||||
if (
|
||||
!isEllipsifiedLabel(spec.text) &&
|
||||
(!bestOverflowNonEllipsified ||
|
||||
overflow < bestOverflowNonEllipsified.overflow ||
|
||||
(overflow === bestOverflowNonEllipsified.overflow &&
|
||||
shouldPrefer(spec, bestOverflowNonEllipsified.spec)))
|
||||
) {
|
||||
bestOverflowNonEllipsified = { overflow, spec };
|
||||
}
|
||||
}
|
||||
|
||||
if (bestFit) {
|
||||
return bestFit;
|
||||
}
|
||||
|
||||
if (
|
||||
bestOverflowNonEllipsified &&
|
||||
bestOverflowAny &&
|
||||
bestOverflowNonEllipsified.overflow <=
|
||||
bestOverflowAny.overflow + CARTESIAN_LABEL_OVERFLOW_PREFERENCE_BUFFER
|
||||
) {
|
||||
return bestOverflowNonEllipsified.spec;
|
||||
}
|
||||
|
||||
return bestOverflowAny!.spec;
|
||||
};
|
||||
|
||||
export const getRotatedTextElementBottom = (
|
||||
element: NonDeletedExcalidrawElement,
|
||||
) => {
|
||||
if (element.type !== "text") {
|
||||
return element.y + element.height;
|
||||
}
|
||||
const rotated = getRotatedBoundingBox(
|
||||
element.width,
|
||||
element.height,
|
||||
element.angle,
|
||||
);
|
||||
return element.y + element.height / 2 + rotated.height / 2;
|
||||
};
|
||||
|
||||
export const chartXLabels = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
backgroundColor: string,
|
||||
layout: CartesianChartLayout,
|
||||
): ChartElements => {
|
||||
const fontFamily = commonProps.fontFamily;
|
||||
const fontSize = FONT_SIZES.sm;
|
||||
const lineHeight = getLineHeight(fontFamily);
|
||||
const fontString = getFontString({ fontFamily, fontSize });
|
||||
const maxRotatedWidth = Math.max(
|
||||
1,
|
||||
layout.slotWidth +
|
||||
layout.gap -
|
||||
CARTESIAN_LABEL_SLOT_PADDING * 2 +
|
||||
CARTESIAN_LABEL_ROTATED_WIDTH_BUFFER,
|
||||
);
|
||||
const axisY = y;
|
||||
|
||||
return (
|
||||
spreadsheet.labels?.map((label, index) => {
|
||||
const labelSpec = getCartesianAxisLabelSpec(
|
||||
label,
|
||||
layout.xLabelMaxWidth,
|
||||
maxRotatedWidth,
|
||||
fontString,
|
||||
lineHeight,
|
||||
);
|
||||
const centerX =
|
||||
x +
|
||||
index * (layout.slotWidth + layout.gap) +
|
||||
layout.gap +
|
||||
layout.slotWidth / 2;
|
||||
const labelY =
|
||||
axisY +
|
||||
CARTESIAN_LABEL_AXIS_CLEARANCE +
|
||||
(labelSpec.rotatedHeight - labelSpec.metrics.height) / 2;
|
||||
|
||||
return newTextElement({
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
text: labelSpec.text,
|
||||
originalText: labelSpec.wrapped ? label : labelSpec.text,
|
||||
autoResize: !labelSpec.wrapped,
|
||||
x: centerX,
|
||||
y: labelY,
|
||||
angle: CARTESIAN_LABEL_ROTATION,
|
||||
fontSize,
|
||||
lineHeight,
|
||||
textAlign: "center",
|
||||
verticalAlign: "top",
|
||||
});
|
||||
}) || []
|
||||
);
|
||||
};
|
||||
|
||||
const chartYLabels = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
backgroundColor: string,
|
||||
layout: CartesianChartLayout,
|
||||
maxValue = Math.max(...spreadsheet.series[0].values),
|
||||
): ChartElements => {
|
||||
const minYLabel = newTextElement({
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
x: x - layout.gap,
|
||||
y: y - layout.gap,
|
||||
text: "0",
|
||||
textAlign: "right",
|
||||
});
|
||||
|
||||
const maxYLabel = newTextElement({
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
x: x - layout.gap,
|
||||
y: y - layout.chartHeight - minYLabel.height / 2,
|
||||
text: maxValue.toLocaleString(),
|
||||
textAlign: "right",
|
||||
});
|
||||
|
||||
return [minYLabel, maxYLabel];
|
||||
};
|
||||
|
||||
const chartLines = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
backgroundColor: string,
|
||||
layout: CartesianChartLayout,
|
||||
): ChartElements => {
|
||||
const { chartWidth, chartHeight } = getChartDimensions(spreadsheet, layout);
|
||||
const xLine = newLinearElement({
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x,
|
||||
y,
|
||||
width: chartWidth,
|
||||
points: [pointFrom(0, 0), pointFrom(chartWidth, 0)],
|
||||
});
|
||||
|
||||
const yLine = newLinearElement({
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x,
|
||||
y,
|
||||
height: chartHeight,
|
||||
points: [pointFrom(0, 0), pointFrom(0, -chartHeight)],
|
||||
});
|
||||
|
||||
const maxLine = newLinearElement({
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x,
|
||||
y: y - layout.chartHeight - layout.gap,
|
||||
strokeStyle: "dotted",
|
||||
width: chartWidth,
|
||||
opacity: GRID_OPACITY,
|
||||
points: [pointFrom(0, 0), pointFrom(chartWidth, 0)],
|
||||
});
|
||||
|
||||
return [xLine, yLine, maxLine];
|
||||
};
|
||||
|
||||
// For the maths behind it https://excalidraw.com/#json=6320864370884608,O_5xfD-Agh32tytHpRJx1g
|
||||
export const chartBaseElements = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
backgroundColor: string,
|
||||
layout: CartesianChartLayout,
|
||||
maxValue = Math.max(...spreadsheet.series[0].values),
|
||||
debug?: boolean,
|
||||
): ChartElements => {
|
||||
const { chartWidth, chartHeight } = getChartDimensions(spreadsheet, layout);
|
||||
|
||||
const title = spreadsheet.title
|
||||
? newTextElement({
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
text: spreadsheet.title,
|
||||
x: x + chartWidth / 2,
|
||||
y: y - layout.chartHeight - layout.gap * 2 - DEFAULT_FONT_SIZE,
|
||||
roundness: null,
|
||||
textAlign: "center",
|
||||
fontSize: FONT_SIZES.xl,
|
||||
fontFamily: FONT_FAMILY["Lilita One"],
|
||||
})
|
||||
: null;
|
||||
|
||||
const debugRect = debug
|
||||
? newElement({
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
type: "rectangle",
|
||||
x,
|
||||
y: y - chartHeight,
|
||||
width: chartWidth,
|
||||
height: chartHeight,
|
||||
strokeColor: COLOR_PALETTE.black,
|
||||
fillStyle: "solid",
|
||||
opacity: 6,
|
||||
})
|
||||
: null;
|
||||
|
||||
return [
|
||||
...(debugRect ? [debugRect] : []),
|
||||
...(title ? [title] : []),
|
||||
...chartXLabels(spreadsheet, x, y, backgroundColor, layout),
|
||||
...chartYLabels(spreadsheet, x, y, backgroundColor, layout, maxValue),
|
||||
...chartLines(spreadsheet, x, y, backgroundColor, layout),
|
||||
];
|
||||
};
|
||||
@@ -0,0 +1,130 @@
|
||||
import { pointFrom } from "@excalidraw/math";
|
||||
|
||||
import { isDevEnv } from "@excalidraw/common";
|
||||
|
||||
import { newElement, newLinearElement } from "@excalidraw/element";
|
||||
|
||||
import type { LocalPoint } from "@excalidraw/math";
|
||||
|
||||
import { GRID_OPACITY, commonProps } from "./charts.constants";
|
||||
import {
|
||||
chartBaseElements,
|
||||
chartXLabels,
|
||||
createSeriesLegend,
|
||||
getBackgroundColor,
|
||||
getCartesianChartLayout,
|
||||
getChartDimensions,
|
||||
getColorOffset,
|
||||
getRotatedTextElementBottom,
|
||||
getSeriesColors,
|
||||
} from "./charts.helpers";
|
||||
|
||||
import type { ChartElements, Spreadsheet } from "./charts.types";
|
||||
|
||||
export const renderLineChart = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
colorSeed?: number,
|
||||
): ChartElements => {
|
||||
const series = spreadsheet.series;
|
||||
const layout = getCartesianChartLayout("line", series.length);
|
||||
const max = Math.max(1, ...series.flatMap((seriesData) => seriesData.values));
|
||||
const colorOffset = getColorOffset(colorSeed);
|
||||
const backgroundColor = getBackgroundColor(colorOffset);
|
||||
const seriesColors = getSeriesColors(series.length, colorOffset);
|
||||
|
||||
const lines = series.map((seriesData, seriesIndex) => {
|
||||
const points = seriesData.values.map((value, valueIndex) =>
|
||||
pointFrom<LocalPoint>(
|
||||
valueIndex * (layout.slotWidth + layout.gap),
|
||||
-(value / max) * layout.chartHeight,
|
||||
),
|
||||
);
|
||||
|
||||
const maxX = Math.max(...points.map((point) => point[0]));
|
||||
const maxY = Math.max(...points.map((point) => point[1]));
|
||||
const minX = Math.min(...points.map((point) => point[0]));
|
||||
const minY = Math.min(...points.map((point) => point[1]));
|
||||
|
||||
return newLinearElement({
|
||||
backgroundColor: "transparent",
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x: x + layout.gap + layout.slotWidth / 2,
|
||||
y: y - layout.gap,
|
||||
height: maxY - minY,
|
||||
width: maxX - minX,
|
||||
strokeColor: seriesColors[seriesIndex],
|
||||
strokeWidth: 2,
|
||||
points,
|
||||
});
|
||||
});
|
||||
|
||||
const dots = series.flatMap((seriesData, seriesIndex) =>
|
||||
seriesData.values.map((value, valueIndex) => {
|
||||
const cx = valueIndex * (layout.slotWidth + layout.gap) + layout.gap / 2;
|
||||
const cy = -(value / max) * layout.chartHeight + layout.gap / 2;
|
||||
return newElement({
|
||||
backgroundColor: seriesColors[seriesIndex],
|
||||
...commonProps,
|
||||
fillStyle: "solid",
|
||||
strokeColor: seriesColors[seriesIndex],
|
||||
strokeWidth: 2,
|
||||
type: "ellipse",
|
||||
x: x + cx + layout.slotWidth / 2,
|
||||
y: y + cy - layout.gap * 2,
|
||||
width: layout.gap,
|
||||
height: layout.gap,
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
const guideValues = series[0].values.map((_, valueIndex) =>
|
||||
Math.max(
|
||||
0,
|
||||
...series.map((seriesData) => seriesData.values[valueIndex] ?? 0),
|
||||
),
|
||||
);
|
||||
const guides = guideValues.map((value, valueIndex) => {
|
||||
const cx = valueIndex * (layout.slotWidth + layout.gap) + layout.gap / 2;
|
||||
const cy = (value / max) * layout.chartHeight + layout.gap / 2 + layout.gap;
|
||||
return newLinearElement({
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x: x + cx + layout.slotWidth / 2 + layout.gap / 2,
|
||||
y: y - cy,
|
||||
height: cy,
|
||||
strokeStyle: "dotted",
|
||||
opacity: GRID_OPACITY,
|
||||
points: [pointFrom(0, 0), pointFrom(0, cy)],
|
||||
});
|
||||
});
|
||||
|
||||
const baseElements = chartBaseElements(
|
||||
spreadsheet,
|
||||
x,
|
||||
y,
|
||||
backgroundColor,
|
||||
layout,
|
||||
max,
|
||||
isDevEnv(),
|
||||
);
|
||||
const xLabels = chartXLabels(spreadsheet, x, y, backgroundColor, layout);
|
||||
const xLabelsBottomY = Math.max(
|
||||
y + layout.gap / 2,
|
||||
...xLabels.map((label) => getRotatedTextElementBottom(label)),
|
||||
);
|
||||
const { chartWidth } = getChartDimensions(spreadsheet, layout);
|
||||
const seriesLegend = createSeriesLegend(
|
||||
series,
|
||||
seriesColors,
|
||||
x + chartWidth / 2,
|
||||
xLabelsBottomY,
|
||||
y + layout.gap * 5,
|
||||
backgroundColor,
|
||||
);
|
||||
|
||||
return [...baseElements, ...lines, ...guides, ...dots, ...seriesLegend];
|
||||
};
|
||||
@@ -0,0 +1,174 @@
|
||||
import { type ParseSpreadsheetResult } from "./charts.types";
|
||||
|
||||
/**
|
||||
* @private exported for testing
|
||||
*/
|
||||
export const tryParseNumber = (s: string): number | null => {
|
||||
const match =
|
||||
/^([-+]?)[$\u20AC\u00A3\u00A5\u20A9]?([-+]?)([\d.,]+)[%]?$/.exec(s);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
return parseFloat(`${(match[1] || match[2]) + match[3]}`.replace(/,/g, ""));
|
||||
};
|
||||
|
||||
const isNumericColumn = (lines: string[][], columnIndex: number) =>
|
||||
lines.slice(1).every((line) => tryParseNumber(line[columnIndex]) !== null);
|
||||
|
||||
/**
|
||||
* @private exported for testing
|
||||
*/
|
||||
export const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
|
||||
const numCols = cells[0].length;
|
||||
|
||||
if (numCols > 2) {
|
||||
const hasHeader = cells[0].every((cell) => tryParseNumber(cell) === null);
|
||||
const rows = hasHeader ? cells.slice(1) : cells;
|
||||
|
||||
if (rows.length < 1) {
|
||||
return { ok: false, reason: "No data rows" };
|
||||
}
|
||||
|
||||
const invalidNumericColumn = rows.some((row) =>
|
||||
row.slice(1).some((value) => tryParseNumber(value) === null),
|
||||
);
|
||||
if (invalidNumericColumn) {
|
||||
return { ok: false, reason: "Value is not numeric" };
|
||||
}
|
||||
|
||||
// When there are more value columns than data rows, the data is in
|
||||
// "wide" format — transpose so columns become labels (dimensions)
|
||||
// and rows become series. This enables e.g. radar charts for wide data.
|
||||
const numValueCols = numCols - 1;
|
||||
if (numValueCols > rows.length) {
|
||||
const labels = hasHeader ? cells[0].slice(1).map((h) => h.trim()) : null;
|
||||
const series = rows.map((row) => ({
|
||||
title: row[0]?.trim() || null,
|
||||
values: row.slice(1).map((v) => tryParseNumber(v)!),
|
||||
}));
|
||||
const title =
|
||||
series.length === 1
|
||||
? series[0].title
|
||||
: hasHeader
|
||||
? cells[0][0].trim() || null
|
||||
: null;
|
||||
return {
|
||||
ok: true,
|
||||
data: { title, labels, series },
|
||||
};
|
||||
}
|
||||
|
||||
const series = cells[0].slice(1).map((seriesTitle, index) => {
|
||||
const valueColumnIndex = index + 1;
|
||||
const fallbackTitle = `Series ${valueColumnIndex}`;
|
||||
return {
|
||||
title: hasHeader ? seriesTitle.trim() || fallbackTitle : fallbackTitle,
|
||||
values: rows.map((row) => tryParseNumber(row[valueColumnIndex])!),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
data: {
|
||||
title: hasHeader ? cells[0][0].trim() || null : null,
|
||||
labels: rows.map((row) => row[0]),
|
||||
series,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (numCols === 1) {
|
||||
if (!isNumericColumn(cells, 0)) {
|
||||
return { ok: false, reason: "Value is not numeric" };
|
||||
}
|
||||
|
||||
const hasHeader = tryParseNumber(cells[0][0]) === null;
|
||||
const title = hasHeader ? cells[0][0] : null;
|
||||
const values = (hasHeader ? cells.slice(1) : cells).map((line) =>
|
||||
tryParseNumber(line[0]),
|
||||
);
|
||||
|
||||
if (values.length < 2) {
|
||||
return { ok: false, reason: "Less than two rows" };
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
data: {
|
||||
title,
|
||||
labels: null,
|
||||
series: [{ title, values: values as number[] }],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const hasHeader = tryParseNumber(cells[0][1]) === null;
|
||||
const rows = hasHeader ? cells.slice(1) : cells;
|
||||
|
||||
if (rows.length < 2) {
|
||||
return { ok: false, reason: "Less than 2 rows" };
|
||||
}
|
||||
|
||||
const invalidNumericColumn = rows.some(
|
||||
(row) => tryParseNumber(row[1]) === null,
|
||||
);
|
||||
if (invalidNumericColumn) {
|
||||
return { ok: false, reason: "Value is not numeric" };
|
||||
}
|
||||
|
||||
const title = hasHeader ? cells[0][1] : null;
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
data: {
|
||||
title,
|
||||
labels: rows.map((row) => row[0]),
|
||||
series: [{ title, values: rows.map((row) => tryParseNumber(row[1])!) }],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const tryParseSpreadsheet = (text: string): ParseSpreadsheetResult => {
|
||||
// Copy/paste from excel, spreadsheets, TSV, CSV, semicolon-separated.
|
||||
const parseDelimitedLines = (delimiter: "\t" | "," | ";") =>
|
||||
text
|
||||
.replace(/\r\n?/g, "\n")
|
||||
.split("\n")
|
||||
.filter((line) => line.trim().length > 0)
|
||||
.map((line) => line.split(delimiter).map((cell) => cell.trim()));
|
||||
|
||||
// Score each delimiter: prefer consistent column counts with the most columns.
|
||||
// A delimiter that produces all single-column rows likely isn't the right one.
|
||||
const candidates = (["\t", ",", ";"] as const).map((delimiter) => {
|
||||
const parsed = parseDelimitedLines(delimiter);
|
||||
const numCols = parsed[0]?.length ?? 0;
|
||||
const isConsistent =
|
||||
parsed.length > 0 && parsed.every((line) => line.length === numCols);
|
||||
return { delimiter, parsed, numCols, isConsistent };
|
||||
});
|
||||
|
||||
// Prefer: consistent + most columns. Among ties, tab > comma > semicolon
|
||||
// (the array order already encodes this priority).
|
||||
const best =
|
||||
candidates.find((c) => c.isConsistent && c.numCols > 1) ??
|
||||
candidates.find((c) => c.isConsistent) ??
|
||||
candidates[0];
|
||||
|
||||
const lines = best.parsed;
|
||||
|
||||
if (lines.length === 0) {
|
||||
return { ok: false, reason: "No values" };
|
||||
}
|
||||
|
||||
const numColsFirstLine = lines[0].length;
|
||||
const isSpreadsheet = lines.every((line) => line.length === numColsFirstLine);
|
||||
|
||||
if (!isSpreadsheet) {
|
||||
return {
|
||||
ok: false,
|
||||
reason: "All rows don't have same number of columns",
|
||||
};
|
||||
}
|
||||
|
||||
return tryParseCells(lines);
|
||||
};
|
||||
@@ -0,0 +1,199 @@
|
||||
import { pointFrom } from "@excalidraw/math";
|
||||
|
||||
import {
|
||||
FONT_FAMILY,
|
||||
FONT_SIZES,
|
||||
getFontString,
|
||||
getLineHeight,
|
||||
ROUGHNESS,
|
||||
} from "@excalidraw/common";
|
||||
|
||||
import {
|
||||
measureText,
|
||||
newLinearElement,
|
||||
newTextElement,
|
||||
} from "@excalidraw/element";
|
||||
|
||||
import type { LocalPoint } from "@excalidraw/math";
|
||||
|
||||
import {
|
||||
BAR_GAP,
|
||||
BAR_HEIGHT,
|
||||
GRID_OPACITY,
|
||||
RADAR_GRID_LEVELS,
|
||||
RADAR_LABEL_OFFSET,
|
||||
commonProps,
|
||||
} from "./charts.constants";
|
||||
import {
|
||||
createRadarAxisLabels,
|
||||
createSeriesLegend,
|
||||
getBackgroundColor,
|
||||
getColorOffset,
|
||||
getRadarDimensions,
|
||||
getRadarDisplayText,
|
||||
getRadarValueScale,
|
||||
getSeriesColors,
|
||||
isSpreadsheetValidForChartType,
|
||||
} from "./charts.helpers";
|
||||
|
||||
import type { ChartElements, Spreadsheet } from "./charts.types";
|
||||
|
||||
export const renderRadarChart = (
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
colorSeed?: number,
|
||||
): ChartElements | null => {
|
||||
if (!isSpreadsheetValidForChartType(spreadsheet, "radar")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const labels =
|
||||
spreadsheet.labels ??
|
||||
spreadsheet.series[0].values.map((_, index) => `Value ${index + 1}`);
|
||||
|
||||
const series = spreadsheet.series;
|
||||
const { normalize, renderSteps } = getRadarValueScale(series, labels.length);
|
||||
const colorOffset = getColorOffset(colorSeed);
|
||||
const backgroundColor = getBackgroundColor(colorOffset);
|
||||
const seriesColors = getSeriesColors(series.length, colorOffset);
|
||||
const { chartWidth, chartHeight } = getRadarDimensions();
|
||||
const centerX = x + chartWidth / 2;
|
||||
const centerY = y - chartHeight / 2;
|
||||
const radius = BAR_HEIGHT / 2;
|
||||
const angles = labels.map(
|
||||
(_, index) => -Math.PI / 2 + (Math.PI * 2 * index) / labels.length,
|
||||
);
|
||||
|
||||
const { axisLabels, axisLabelTopY, axisLabelBottomY } = createRadarAxisLabels(
|
||||
labels,
|
||||
angles,
|
||||
centerX,
|
||||
centerY,
|
||||
radius,
|
||||
backgroundColor,
|
||||
);
|
||||
|
||||
const titleFontFamily = FONT_FAMILY["Lilita One"];
|
||||
const titleFontSize = FONT_SIZES.xl;
|
||||
const titleLineHeight = getLineHeight(titleFontFamily);
|
||||
const titleFontString = getFontString({
|
||||
fontFamily: titleFontFamily,
|
||||
fontSize: titleFontSize,
|
||||
});
|
||||
const titleText = spreadsheet.title
|
||||
? getRadarDisplayText(
|
||||
spreadsheet.title,
|
||||
titleFontString,
|
||||
chartWidth + RADAR_LABEL_OFFSET * 2,
|
||||
)
|
||||
: null;
|
||||
const titleTextMetrics = titleText
|
||||
? measureText(titleText, titleFontString, titleLineHeight)
|
||||
: null;
|
||||
const title = titleText
|
||||
? newTextElement({
|
||||
backgroundColor,
|
||||
...commonProps,
|
||||
text: titleText,
|
||||
originalText: spreadsheet.title ?? titleText,
|
||||
x: x + chartWidth / 2,
|
||||
y: axisLabelTopY - RADAR_LABEL_OFFSET - titleTextMetrics!.height / 2,
|
||||
fontFamily: titleFontFamily,
|
||||
fontSize: titleFontSize,
|
||||
lineHeight: titleLineHeight,
|
||||
textAlign: "center",
|
||||
})
|
||||
: null;
|
||||
|
||||
const radarGridLines = renderSteps
|
||||
? Array.from({ length: RADAR_GRID_LEVELS }, (_, levelIndex) => {
|
||||
const levelRatio = (levelIndex + 1) / RADAR_GRID_LEVELS;
|
||||
const levelRadius = radius * levelRatio;
|
||||
const points = angles.map((angle) =>
|
||||
pointFrom<LocalPoint>(
|
||||
Math.cos(angle) * levelRadius,
|
||||
Math.sin(angle) * levelRadius,
|
||||
),
|
||||
);
|
||||
points.push(pointFrom(points[0][0], points[0][1]));
|
||||
|
||||
return newLinearElement({
|
||||
backgroundColor: "transparent",
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x: centerX,
|
||||
y: centerY,
|
||||
width: levelRadius * 2,
|
||||
height: levelRadius * 2,
|
||||
strokeStyle: "solid",
|
||||
roughness: ROUGHNESS.architect,
|
||||
opacity: GRID_OPACITY,
|
||||
polygon: true,
|
||||
points,
|
||||
});
|
||||
})
|
||||
: [];
|
||||
|
||||
const spokes = angles.map((angle) => {
|
||||
const px = Math.cos(angle) * radius;
|
||||
const py = Math.sin(angle) * radius;
|
||||
return newLinearElement({
|
||||
backgroundColor: "transparent",
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x: centerX,
|
||||
y: centerY,
|
||||
width: Math.abs(px),
|
||||
height: Math.abs(py),
|
||||
strokeStyle: "solid",
|
||||
roughness: ROUGHNESS.architect,
|
||||
opacity: GRID_OPACITY,
|
||||
points: [pointFrom(0, 0), pointFrom(px, py)],
|
||||
});
|
||||
});
|
||||
|
||||
const seriesPolygons = series.map((seriesData, index) => {
|
||||
const points = angles.map((angle, axisIndex) => {
|
||||
const value = seriesData.values[axisIndex] ?? 0;
|
||||
const pointRadius = normalize(value, axisIndex) * radius;
|
||||
return pointFrom<LocalPoint>(
|
||||
Math.cos(angle) * pointRadius,
|
||||
Math.sin(angle) * pointRadius,
|
||||
);
|
||||
});
|
||||
points.push(pointFrom(points[0][0], points[0][1]));
|
||||
|
||||
return newLinearElement({
|
||||
backgroundColor: "transparent",
|
||||
...commonProps,
|
||||
type: "line",
|
||||
x: centerX,
|
||||
y: centerY,
|
||||
width: radius * 2,
|
||||
height: radius * 2,
|
||||
strokeColor: seriesColors[index],
|
||||
strokeWidth: 2,
|
||||
polygon: true,
|
||||
points,
|
||||
});
|
||||
});
|
||||
|
||||
const seriesLegend = createSeriesLegend(
|
||||
series,
|
||||
seriesColors,
|
||||
centerX,
|
||||
axisLabelBottomY,
|
||||
y + BAR_GAP * 5,
|
||||
backgroundColor,
|
||||
);
|
||||
|
||||
return [
|
||||
...(title ? [title] : []),
|
||||
...axisLabels,
|
||||
...radarGridLines,
|
||||
...spokes,
|
||||
...seriesPolygons,
|
||||
...seriesLegend,
|
||||
];
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { NonDeletedExcalidrawElement } from "@excalidraw/element/types";
|
||||
|
||||
export type ChartElements = readonly NonDeletedExcalidrawElement[];
|
||||
|
||||
export interface Spreadsheet {
|
||||
title: string | null;
|
||||
labels: string[] | null;
|
||||
series: SpreadsheetSeries[];
|
||||
}
|
||||
|
||||
export interface SpreadsheetSeries {
|
||||
title: string | null;
|
||||
values: number[];
|
||||
}
|
||||
|
||||
export type ParseSpreadsheetResult =
|
||||
| { ok: false; reason: string }
|
||||
| { ok: true; data: Spreadsheet };
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { ChartType } from "@excalidraw/element/types";
|
||||
|
||||
import { renderBarChart } from "./charts.bar";
|
||||
import { renderLineChart } from "./charts.line";
|
||||
import {
|
||||
tryParseCells,
|
||||
tryParseNumber,
|
||||
tryParseSpreadsheet,
|
||||
} from "./charts.parse";
|
||||
import { renderRadarChart } from "./charts.radar";
|
||||
|
||||
import type { ChartElements, Spreadsheet } from "./charts.types";
|
||||
|
||||
export {
|
||||
type ParseSpreadsheetResult,
|
||||
type Spreadsheet,
|
||||
type SpreadsheetSeries,
|
||||
type ChartElements,
|
||||
} from "./charts.types";
|
||||
|
||||
export { isSpreadsheetValidForChartType } from "./charts.helpers";
|
||||
export { tryParseCells, tryParseNumber, tryParseSpreadsheet };
|
||||
|
||||
export const renderSpreadsheet = (
|
||||
chartType: ChartType,
|
||||
spreadsheet: Spreadsheet,
|
||||
x: number,
|
||||
y: number,
|
||||
colorSeed?: number,
|
||||
): ChartElements | null => {
|
||||
if (chartType === "line") {
|
||||
return renderLineChart(spreadsheet, x, y, colorSeed);
|
||||
}
|
||||
if (chartType === "radar") {
|
||||
return renderRadarChart(spreadsheet, x, y, colorSeed);
|
||||
}
|
||||
return renderBarChart(spreadsheet, x, y, colorSeed);
|
||||
};
|
||||
@@ -155,67 +155,4 @@ describe("parseClipboard()", () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("should parse spreadsheet from either text/plain and text/html", async () => {
|
||||
let clipboardData;
|
||||
// -------------------------------------------------------------------------
|
||||
clipboardData = await parseClipboard(
|
||||
await parseDataTransferEvent(
|
||||
createPasteEvent({
|
||||
types: {
|
||||
"text/plain": `a b
|
||||
1 2
|
||||
4 5
|
||||
7 10`,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
expect(clipboardData.spreadsheet).toEqual({
|
||||
title: "b",
|
||||
labels: ["1", "4", "7"],
|
||||
values: [2, 5, 10],
|
||||
});
|
||||
// -------------------------------------------------------------------------
|
||||
clipboardData = await parseClipboard(
|
||||
await parseDataTransferEvent(
|
||||
createPasteEvent({
|
||||
types: {
|
||||
"text/html": `a b
|
||||
1 2
|
||||
4 5
|
||||
7 10`,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
expect(clipboardData.spreadsheet).toEqual({
|
||||
title: "b",
|
||||
labels: ["1", "4", "7"],
|
||||
values: [2, 5, 10],
|
||||
});
|
||||
// -------------------------------------------------------------------------
|
||||
clipboardData = await parseClipboard(
|
||||
await parseDataTransferEvent(
|
||||
createPasteEvent({
|
||||
types: {
|
||||
"text/html": `<html>
|
||||
<body>
|
||||
<!--StartFragment--><google-sheets-html-origin><style type="text/css"><!--td {border: 1px solid #cccccc;}br {mso-data-placement:same-cell;}--></style><table xmlns="http://www.w3.org/1999/xhtml" cellspacing="0" cellpadding="0" dir="ltr" border="1" style="table-layout:fixed;font-size:10pt;font-family:Arial;width:0px;border-collapse:collapse;border:none"><colgroup><col width="100"/><col width="100"/></colgroup><tbody><tr style="height:21px;"><td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;" data-sheets-value="{"1":2,"2":"a"}">a</td><td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;" data-sheets-value="{"1":2,"2":"b"}">b</td></tr><tr style="height:21px;"><td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;text-align:right;" data-sheets-value="{"1":3,"3":1}">1</td><td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;text-align:right;" data-sheets-value="{"1":3,"3":2}">2</td></tr><tr style="height:21px;"><td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;text-align:right;" data-sheets-value="{"1":3,"3":4}">4</td><td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;text-align:right;" data-sheets-value="{"1":3,"3":5}">5</td></tr><tr style="height:21px;"><td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;text-align:right;" data-sheets-value="{"1":3,"3":7}">7</td><td style="overflow:hidden;padding:2px 3px 2px 3px;vertical-align:bottom;text-align:right;" data-sheets-value="{"1":3,"3":10}">10</td></tr></tbody></table><!--EndFragment-->
|
||||
</body>
|
||||
</html>`,
|
||||
"text/plain": `a b
|
||||
1 2
|
||||
4 5
|
||||
7 10`,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
expect(clipboardData.spreadsheet).toEqual({
|
||||
title: "b",
|
||||
labels: ["1", "4", "7"],
|
||||
values: [2, 5, 10],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,12 +33,8 @@ import {
|
||||
normalizeFile,
|
||||
} from "./data/blob";
|
||||
|
||||
import { tryParseSpreadsheet, VALID_SPREADSHEET } from "./charts";
|
||||
|
||||
import type { FileSystemHandle } from "./data/filesystem";
|
||||
|
||||
import type { Spreadsheet } from "./charts";
|
||||
|
||||
import type { BinaryFiles } from "./types";
|
||||
|
||||
type ElementsClipboard = {
|
||||
@@ -50,7 +46,6 @@ type ElementsClipboard = {
|
||||
export type PastedMixedContent = { type: "text" | "imageUrl"; value: string }[];
|
||||
|
||||
export interface ClipboardData {
|
||||
spreadsheet?: Spreadsheet;
|
||||
elements?: readonly ExcalidrawElement[];
|
||||
files?: BinaryFiles;
|
||||
text?: string;
|
||||
@@ -215,16 +210,6 @@ export const copyToClipboard = async (
|
||||
);
|
||||
};
|
||||
|
||||
const parsePotentialSpreadsheet = (
|
||||
text: string,
|
||||
): { spreadsheet: Spreadsheet } | { errorMessage: string } | null => {
|
||||
const result = tryParseSpreadsheet(text);
|
||||
if (result.type === VALID_SPREADSHEET) {
|
||||
return { spreadsheet: result.spreadsheet };
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/** internal, specific to parsing paste events. Do not reuse. */
|
||||
function parseHTMLTree(el: ChildNode) {
|
||||
let result: PastedMixedContent = [];
|
||||
@@ -551,19 +536,6 @@ export const parseClipboard = async (
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// if system clipboard contains spreadsheet, use it even though it's
|
||||
// technically possible it's staler than in-app clipboard
|
||||
const spreadsheetResult =
|
||||
!isPlainPaste && parsePotentialSpreadsheet(parsedEventData.value);
|
||||
|
||||
if (spreadsheetResult) {
|
||||
return spreadsheetResult;
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
try {
|
||||
const systemClipboardData = JSON.parse(parsedEventData.value);
|
||||
const programmaticAPI =
|
||||
|
||||
@@ -226,7 +226,7 @@ export const SelectedShapeActions = ({
|
||||
{(appState.activeTool.type === "text" ||
|
||||
targetElements.some(isTextElement)) && (
|
||||
<>
|
||||
{renderAction("changeFontFamily")}
|
||||
<fieldset>{renderAction("changeFontFamily")}</fieldset>
|
||||
{renderAction("changeFontSize")}
|
||||
{(appState.activeTool.type === "text" ||
|
||||
suppportsHorizontalAlign(targetElements, elementsMap)) &&
|
||||
@@ -1081,8 +1081,9 @@ export const ShapesSwitcher = ({
|
||||
return (
|
||||
<>
|
||||
{getToolbarTools(app).map(
|
||||
({ value, icon, key, numericKey, fillable }, index) => {
|
||||
({ value, icon, key, numericKey, fillable, toolbar }) => {
|
||||
if (
|
||||
toolbar === false ||
|
||||
UIOptions.tools?.[
|
||||
value as Extract<
|
||||
typeof value,
|
||||
@@ -1099,6 +1100,9 @@ export const ShapesSwitcher = ({
|
||||
const shortcut = letter
|
||||
? `${letter} ${t("helpDialog.or")} ${numericKey}`
|
||||
: `${numericKey}`;
|
||||
const keybindingLabel =
|
||||
value === "hand" ? undefined : numericKey || letter;
|
||||
|
||||
// when in compact styles panel mode (tablet)
|
||||
// use a ToolPopover for selection/lasso toggle as well
|
||||
if (
|
||||
@@ -1143,7 +1147,7 @@ export const ShapesSwitcher = ({
|
||||
checked={activeTool.type === value}
|
||||
name="editor-current-shape"
|
||||
title={`${capitalizeString(label)} — ${shortcut}`}
|
||||
keyBindingLabel={numericKey || letter}
|
||||
keyBindingLabel={keybindingLabel}
|
||||
aria-label={capitalizeString(label)}
|
||||
aria-keyshortcuts={shortcut}
|
||||
data-testid={`toolbar-${value}`}
|
||||
|
||||
@@ -108,6 +108,7 @@ import {
|
||||
loadDesktopUIModePreference,
|
||||
setDesktopUIMode,
|
||||
isSelectionLikeTool,
|
||||
oneOf,
|
||||
} from "@excalidraw/common";
|
||||
|
||||
import {
|
||||
@@ -118,7 +119,6 @@ import {
|
||||
fixBindingsAfterDeletion,
|
||||
getHoveredElementForBinding,
|
||||
isBindingEnabled,
|
||||
shouldEnableBindingForPointerEvent,
|
||||
updateBoundElements,
|
||||
LinearElementEditor,
|
||||
newElementWith,
|
||||
@@ -318,6 +318,8 @@ import {
|
||||
actionToggleElementLock,
|
||||
actionToggleLinearEditor,
|
||||
actionToggleObjectsSnapMode,
|
||||
actionToggleArrowBinding,
|
||||
actionToggleMidpointSnapping,
|
||||
actionToggleCropEditor,
|
||||
} from "../actions";
|
||||
import { actionWrapTextInContainer } from "../actions/actionBoundText";
|
||||
@@ -424,6 +426,8 @@ import { EraserTrail } from "../eraser";
|
||||
|
||||
import { getShortcutKey } from "../shortcut";
|
||||
|
||||
import { tryParseSpreadsheet } from "../charts";
|
||||
|
||||
import ConvertElementTypePopup, {
|
||||
getConversionTypeFromElements,
|
||||
convertElementTypePopupAtom,
|
||||
@@ -1219,12 +1223,14 @@ class App extends React.Component<AppProps, AppState> {
|
||||
if (
|
||||
hitElement &&
|
||||
isIframeLikeElement(hitElement) &&
|
||||
this.isIframeLikeElementCenter(
|
||||
hitElement,
|
||||
moveEvent,
|
||||
scenePointer.x,
|
||||
scenePointer.y,
|
||||
)
|
||||
(this.state.viewModeEnabled ||
|
||||
this.state.activeTool.type === "laser" ||
|
||||
this.isIframeLikeElementCenter(
|
||||
hitElement,
|
||||
moveEvent,
|
||||
scenePointer.x,
|
||||
scenePointer.y,
|
||||
))
|
||||
) {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
||||
this.setState({
|
||||
@@ -1239,61 +1245,72 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
/** @returns true if iframe-like element click handled */
|
||||
private handleIframeLikeCenterClick(): boolean {
|
||||
if (!this.lastPointerDownEvent || !this.lastPointerUpEvent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const scenePointerStart = viewportCoordsToSceneCoords(
|
||||
{
|
||||
clientX: this.lastPointerDownEvent.clientX,
|
||||
clientY: this.lastPointerDownEvent.clientY,
|
||||
},
|
||||
this.state,
|
||||
);
|
||||
const scenePointerEnd = viewportCoordsToSceneCoords(
|
||||
{
|
||||
clientX: this.lastPointerUpEvent.clientX,
|
||||
clientY: this.lastPointerUpEvent.clientY,
|
||||
},
|
||||
this.state,
|
||||
);
|
||||
|
||||
const hitElementStart = this.getElementAtPosition(
|
||||
scenePointerStart.x,
|
||||
scenePointerStart.y,
|
||||
);
|
||||
|
||||
const hitElementEnd = this.getElementAtPosition(
|
||||
scenePointerEnd.x,
|
||||
scenePointerEnd.y,
|
||||
);
|
||||
|
||||
if (
|
||||
!hitElementStart ||
|
||||
!hitElementEnd ||
|
||||
hitElementStart !== hitElementEnd ||
|
||||
this.lastPointerUpEvent.timeStamp - this.lastPointerDownEvent.timeStamp >
|
||||
300 ||
|
||||
gesture.pointers.size > 1 ||
|
||||
!isIframeLikeElement(hitElementStart) ||
|
||||
!isIframeLikeElement(hitElementEnd) ||
|
||||
!this.isIframeLikeElementCenter(
|
||||
hitElementStart,
|
||||
this.lastPointerUpEvent,
|
||||
scenePointerStart.x,
|
||||
scenePointerStart.y,
|
||||
) ||
|
||||
!this.isIframeLikeElementCenter(
|
||||
hitElementEnd,
|
||||
this.lastPointerUpEvent,
|
||||
scenePointerEnd.x,
|
||||
scenePointerEnd.y,
|
||||
)
|
||||
!this.lastPointerDownEvent ||
|
||||
!this.lastPointerUpEvent ||
|
||||
// middle-click or something other than primary
|
||||
this.lastPointerDownEvent.button !== POINTER_BUTTON.MAIN ||
|
||||
// panning
|
||||
isHoldingSpace ||
|
||||
// wrong tool
|
||||
!oneOf(this.state.activeTool.type, ["laser", "selection", "lasso"])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const iframeLikeElement = hitElementEnd;
|
||||
const viewportClickStart_scenePoint = pointFrom(
|
||||
viewportCoordsToSceneCoords(
|
||||
{
|
||||
clientX: this.lastPointerDownEvent.clientX,
|
||||
clientY: this.lastPointerDownEvent.clientY,
|
||||
},
|
||||
this.state,
|
||||
),
|
||||
);
|
||||
const viewportClickEnd_scenePoint = pointFrom(
|
||||
viewportCoordsToSceneCoords(
|
||||
{
|
||||
clientX: this.lastPointerUpEvent.clientX,
|
||||
clientY: this.lastPointerUpEvent.clientY,
|
||||
},
|
||||
this.state,
|
||||
),
|
||||
);
|
||||
|
||||
const draggedDistance = pointDistance(
|
||||
viewportClickStart_scenePoint,
|
||||
viewportClickEnd_scenePoint,
|
||||
);
|
||||
|
||||
if (draggedDistance > DRAGGING_THRESHOLD) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hitElement = this.getElementAtPosition(
|
||||
viewportClickStart_scenePoint[0],
|
||||
viewportClickStart_scenePoint[1],
|
||||
);
|
||||
|
||||
const shouldActivate =
|
||||
hitElement &&
|
||||
this.lastPointerUpEvent.timeStamp - this.lastPointerDownEvent.timeStamp <=
|
||||
300 &&
|
||||
gesture.pointers.size < 2 &&
|
||||
isIframeLikeElement(hitElement) &&
|
||||
(this.state.viewModeEnabled ||
|
||||
this.state.activeTool.type === "laser" ||
|
||||
this.isIframeLikeElementCenter(
|
||||
hitElement,
|
||||
this.lastPointerUpEvent,
|
||||
viewportClickEnd_scenePoint[0],
|
||||
viewportClickEnd_scenePoint[1],
|
||||
));
|
||||
|
||||
if (!shouldActivate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const iframeLikeElement = hitElement;
|
||||
|
||||
if (
|
||||
this.state.activeEmbeddable?.element === iframeLikeElement &&
|
||||
@@ -2718,7 +2735,9 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
private onBlur = withBatchedUpdates(() => {
|
||||
isHoldingSpace = false;
|
||||
this.setState({ isBindingEnabled: true });
|
||||
this.setState({
|
||||
isBindingEnabled: this.state.bindingPreference === "enabled",
|
||||
});
|
||||
});
|
||||
|
||||
private onUnload = () => {
|
||||
@@ -3528,14 +3547,19 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
|
||||
// ------------------- Spreadsheet -------------------
|
||||
if (data.spreadsheet && !isPlainPaste) {
|
||||
this.setState({
|
||||
pasteDialog: {
|
||||
data: data.spreadsheet,
|
||||
shown: true,
|
||||
},
|
||||
});
|
||||
return;
|
||||
|
||||
if (!isPlainPaste && data.text) {
|
||||
const result = tryParseSpreadsheet(data.text);
|
||||
if (result.ok) {
|
||||
this.setState({
|
||||
openDialog: {
|
||||
name: "charts",
|
||||
data: result.data,
|
||||
rawText: data.text,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------- Images or SVG code -------------------
|
||||
@@ -4844,17 +4868,87 @@ class App extends React.Component<AppProps, AppState> {
|
||||
return;
|
||||
}
|
||||
|
||||
// view mode hardcoded from upstream -> disable tool switching for now
|
||||
const shouldPreventToolSwitching = this.props.viewModeEnabled === true;
|
||||
|
||||
if (
|
||||
!shouldPreventToolSwitching &&
|
||||
this.state.viewModeEnabled &&
|
||||
event.key === KEYS.ESCAPE
|
||||
) {
|
||||
this.setActiveTool({ type: "selection" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!shouldPreventToolSwitching &&
|
||||
!event.ctrlKey &&
|
||||
!event.altKey &&
|
||||
!event.metaKey &&
|
||||
!this.state.newElement &&
|
||||
!this.state.selectionElement &&
|
||||
!this.state.selectedElementsAreBeingDragged
|
||||
) {
|
||||
const shape = findShapeByKey(event.key, this);
|
||||
|
||||
if (this.state.viewModeEnabled && !oneOf(shape, ["laser", "hand"])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shape) {
|
||||
if (this.state.activeTool.type !== shape) {
|
||||
trackEvent(
|
||||
"toolbar",
|
||||
shape,
|
||||
`keyboard (${
|
||||
this.editorInterface.formFactor === "phone"
|
||||
? "mobile"
|
||||
: "desktop"
|
||||
})`,
|
||||
);
|
||||
}
|
||||
if (shape === "arrow" && this.state.activeTool.type === "arrow") {
|
||||
this.setState((prevState) => ({
|
||||
currentItemArrowType:
|
||||
prevState.currentItemArrowType === ARROW_TYPE.sharp
|
||||
? ARROW_TYPE.round
|
||||
: prevState.currentItemArrowType === ARROW_TYPE.round
|
||||
? ARROW_TYPE.elbow
|
||||
: ARROW_TYPE.sharp,
|
||||
}));
|
||||
}
|
||||
|
||||
if (shape === "lasso" && this.state.activeTool.type === "laser") {
|
||||
this.setActiveTool({
|
||||
type: this.state.preferredSelectionTool.type,
|
||||
});
|
||||
} else {
|
||||
this.setActiveTool({ type: shape });
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
|
||||
return;
|
||||
} else if (event.key === KEYS.Q) {
|
||||
this.toggleLock("keyboard");
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state.viewModeEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event[KEYS.CTRL_OR_CMD] && this.state.isBindingEnabled) {
|
||||
if (event[KEYS.CTRL_OR_CMD] && !event.repeat) {
|
||||
if (getFeatureFlag("COMPLEX_BINDINGS")) {
|
||||
this.resetDelayedBindMode();
|
||||
}
|
||||
|
||||
flushSync(() => {
|
||||
this.setState({ isBindingEnabled: false });
|
||||
this.setState({
|
||||
isBindingEnabled: this.state.bindingPreference !== "enabled",
|
||||
});
|
||||
});
|
||||
|
||||
maybeHandleArrowPointlikeDrag({ app: this, event });
|
||||
@@ -4977,44 +5071,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
!event.ctrlKey &&
|
||||
!event.altKey &&
|
||||
!event.metaKey &&
|
||||
!this.state.newElement &&
|
||||
!this.state.selectionElement &&
|
||||
!this.state.selectedElementsAreBeingDragged
|
||||
) {
|
||||
const shape = findShapeByKey(event.key, this);
|
||||
if (shape) {
|
||||
if (this.state.activeTool.type !== shape) {
|
||||
trackEvent(
|
||||
"toolbar",
|
||||
shape,
|
||||
`keyboard (${
|
||||
this.editorInterface.formFactor === "phone"
|
||||
? "mobile"
|
||||
: "desktop"
|
||||
})`,
|
||||
);
|
||||
}
|
||||
if (shape === "arrow" && this.state.activeTool.type === "arrow") {
|
||||
this.setState((prevState) => ({
|
||||
currentItemArrowType:
|
||||
prevState.currentItemArrowType === ARROW_TYPE.sharp
|
||||
? ARROW_TYPE.round
|
||||
: prevState.currentItemArrowType === ARROW_TYPE.round
|
||||
? ARROW_TYPE.elbow
|
||||
: ARROW_TYPE.sharp,
|
||||
}));
|
||||
}
|
||||
this.setActiveTool({ type: shape });
|
||||
event.stopPropagation();
|
||||
} else if (event.key === KEYS.Q) {
|
||||
this.toggleLock("keyboard");
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
if (event.key === KEYS.SPACE && gesture.pointers.size === 0) {
|
||||
isHoldingSpace = true;
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.GRAB);
|
||||
@@ -5078,15 +5136,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
}
|
||||
|
||||
if (event.key === KEYS.K && !event.altKey && !event[KEYS.CTRL_OR_CMD]) {
|
||||
if (this.state.activeTool.type === "laser") {
|
||||
this.setActiveTool({ type: this.state.preferredSelectionTool.type });
|
||||
} else {
|
||||
this.setActiveTool({ type: "laser" });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
event[KEYS.CTRL_OR_CMD] &&
|
||||
(event.key === KEYS.BACKSPACE || event.key === KEYS.DELETE)
|
||||
@@ -5113,7 +5162,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
private onKeyUp = withBatchedUpdates((event: KeyboardEvent) => {
|
||||
if (event.key === KEYS.SPACE) {
|
||||
if (
|
||||
this.state.viewModeEnabled ||
|
||||
(this.state.viewModeEnabled &&
|
||||
this.state.activeTool.type !== "laser") ||
|
||||
this.state.openDialog?.name === "elementLinkSelector"
|
||||
) {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.GRAB);
|
||||
@@ -5172,10 +5222,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!event[KEYS.CTRL_OR_CMD] && !this.state.isBindingEnabled) {
|
||||
flushSync(() => {
|
||||
this.setState({ isBindingEnabled: true });
|
||||
});
|
||||
if (!event[KEYS.CTRL_OR_CMD]) {
|
||||
const preferenceEnabled = this.state.bindingPreference === "enabled";
|
||||
if (this.state.isBindingEnabled !== preferenceEnabled) {
|
||||
flushSync(() => {
|
||||
this.setState({ isBindingEnabled: preferenceEnabled });
|
||||
});
|
||||
}
|
||||
|
||||
maybeHandleArrowPointlikeDrag({ app: this, event });
|
||||
}
|
||||
@@ -6227,9 +6280,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
};
|
||||
|
||||
private redirectToLink = (
|
||||
private handleElementLinkClick = (
|
||||
event: React.PointerEvent<HTMLCanvasElement>,
|
||||
isTouchScreen: boolean,
|
||||
) => {
|
||||
const draggedDistance = pointDistance(
|
||||
pointFrom(
|
||||
@@ -6803,6 +6855,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
}
|
||||
|
||||
if (isEraserActive(this.state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hitElementMightBeLocked = this.getElementAtPosition(
|
||||
scenePointerX,
|
||||
scenePointerY,
|
||||
@@ -6819,18 +6875,25 @@ class App extends React.Component<AppProps, AppState> {
|
||||
hitElement = hitElementMightBeLocked;
|
||||
}
|
||||
|
||||
this.hitLinkElement = this.getElementLinkAtPosition(
|
||||
scenePointer,
|
||||
hitElementMightBeLocked,
|
||||
);
|
||||
if (isEraserActive(this.state)) {
|
||||
return;
|
||||
if (
|
||||
!this.handleIframeLikeElementHover({
|
||||
hitElement,
|
||||
scenePointer,
|
||||
moveEvent: event,
|
||||
})
|
||||
) {
|
||||
this.hitLinkElement = this.getElementLinkAtPosition(
|
||||
scenePointer,
|
||||
hitElementMightBeLocked,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
this.hitLinkElement &&
|
||||
!this.state.selectedElementIds[this.hitLinkElement.id]
|
||||
) {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.POINTER);
|
||||
|
||||
showHyperlinkTooltip(
|
||||
this.hitLinkElement,
|
||||
this.state,
|
||||
@@ -6839,11 +6902,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
} else {
|
||||
hideHyperlinkToolip();
|
||||
if (isLaserTool) {
|
||||
this.handleIframeLikeElementHover({
|
||||
hitElement,
|
||||
scenePointer,
|
||||
moveEvent: event,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (
|
||||
@@ -6878,15 +6936,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
!hitElement?.locked
|
||||
) {
|
||||
if (
|
||||
!this.handleIframeLikeElementHover({
|
||||
hitElement,
|
||||
scenePointer,
|
||||
moveEvent: event,
|
||||
}) &&
|
||||
(!hitElement ||
|
||||
// Elbow arrows can only be moved when unconnected
|
||||
!isElbowArrow(hitElement) ||
|
||||
!(hitElement.startBinding || hitElement.endBinding))
|
||||
!hitElement ||
|
||||
// Elbow arrows can only be moved when unconnected
|
||||
!isElbowArrow(hitElement) ||
|
||||
!(hitElement.startBinding || hitElement.endBinding)
|
||||
) {
|
||||
if (
|
||||
this.state.activeTool.type !== "lasso" ||
|
||||
@@ -7093,6 +7146,14 @@ class App extends React.Component<AppProps, AppState> {
|
||||
private handleCanvasPointerDown = (
|
||||
event: React.PointerEvent<HTMLElement>,
|
||||
) => {
|
||||
// If Ctrl is not held, ensure isBindingEnabled reflects the user preference.
|
||||
if (!event.ctrlKey) {
|
||||
const preferenceEnabled = this.state.bindingPreference === "enabled";
|
||||
if (this.state.isBindingEnabled !== preferenceEnabled) {
|
||||
this.setState({ isBindingEnabled: preferenceEnabled });
|
||||
}
|
||||
}
|
||||
|
||||
const scenePointer = viewportCoordsToSceneCoords(event, this.state);
|
||||
const { x: scenePointerX, y: scenePointerY } = scenePointer;
|
||||
this.lastPointerMoveCoords = {
|
||||
@@ -7313,7 +7374,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
|
||||
this.clearSelectionIfNotUsingSelection();
|
||||
this.updateBindingEnabledOnPointerMove(event);
|
||||
|
||||
if (this.handleSelectionOnPointerDown(event, pointerDownState)) {
|
||||
return;
|
||||
@@ -7536,6 +7596,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.removePointer(event);
|
||||
this.lastPointerUpEvent = event;
|
||||
|
||||
if (!event.ctrlKey) {
|
||||
const preferenceEnabled = this.state.bindingPreference === "enabled";
|
||||
if (this.state.isBindingEnabled !== preferenceEnabled) {
|
||||
this.setState({ isBindingEnabled: preferenceEnabled });
|
||||
}
|
||||
}
|
||||
|
||||
const scenePointer = viewportCoordsToSceneCoords(
|
||||
{ clientX: event.clientX, clientY: event.clientY },
|
||||
this.state,
|
||||
@@ -7568,7 +7635,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.hitLinkElement &&
|
||||
!this.state.selectedElementIds[this.hitLinkElement.id]
|
||||
) {
|
||||
this.redirectToLink(event, this.editorInterface.isTouchScreen);
|
||||
this.handleElementLinkClick(event);
|
||||
} else if (this.state.viewModeEnabled) {
|
||||
this.setState({
|
||||
activeEmbeddable: null,
|
||||
@@ -7628,7 +7695,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
(event.button === POINTER_BUTTON.WHEEL ||
|
||||
(event.button === POINTER_BUTTON.MAIN && isHoldingSpace) ||
|
||||
isHandToolActive(this.state) ||
|
||||
this.state.viewModeEnabled)
|
||||
(this.state.viewModeEnabled &&
|
||||
this.state.activeTool.type !== "laser"))
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
@@ -7706,7 +7774,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
lastPointerUp = null;
|
||||
isPanning = false;
|
||||
if (!isHoldingSpace) {
|
||||
if (this.state.viewModeEnabled) {
|
||||
if (
|
||||
this.state.viewModeEnabled &&
|
||||
this.state.activeTool.type !== "laser"
|
||||
) {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.GRAB);
|
||||
} else {
|
||||
setCursorForShape(this.interactiveCanvas, this.state);
|
||||
@@ -8587,7 +8658,9 @@ class App extends React.Component<AppProps, AppState> {
|
||||
): void => {
|
||||
if (event.ctrlKey) {
|
||||
flushSync(() => {
|
||||
this.setState({ isBindingEnabled: false });
|
||||
this.setState({
|
||||
isBindingEnabled: this.state.bindingPreference !== "enabled",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11281,15 +11354,6 @@ class App extends React.Component<AppProps, AppState> {
|
||||
this.addNewImagesToImageCache();
|
||||
}, IMAGE_RENDER_TIMEOUT);
|
||||
|
||||
private updateBindingEnabledOnPointerMove = (
|
||||
event: React.PointerEvent<HTMLElement>,
|
||||
) => {
|
||||
const shouldEnableBinding = shouldEnableBindingForPointerEvent(event);
|
||||
if (this.state.isBindingEnabled !== shouldEnableBinding) {
|
||||
this.setState({ isBindingEnabled: shouldEnableBinding });
|
||||
}
|
||||
};
|
||||
|
||||
private clearSelection(hitElement: ExcalidrawElement | null): void {
|
||||
this.setState((prevState) => ({
|
||||
selectedElementIds: makeNextSelectedElementIds({}, prevState),
|
||||
@@ -12051,6 +12115,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
CONTEXT_MENU_SEPARATOR,
|
||||
actionToggleGridMode,
|
||||
actionToggleObjectsSnapMode,
|
||||
actionToggleArrowBinding,
|
||||
actionToggleMidpointSnapping,
|
||||
actionToggleZenMode,
|
||||
actionToggleViewMode,
|
||||
actionToggleStats,
|
||||
|
||||
@@ -15,7 +15,7 @@ export type CommandPaletteItem = {
|
||||
category: string;
|
||||
order?: number;
|
||||
predicate?: boolean | Action["predicate"];
|
||||
shortcut?: string;
|
||||
shortcut?: string | null;
|
||||
/** if false, command will not show while in view mode */
|
||||
viewMode?: boolean;
|
||||
perform: (data: {
|
||||
|
||||
@@ -20,7 +20,6 @@ import type { NonDeletedExcalidrawElement } from "@excalidraw/element/types";
|
||||
|
||||
import { actionToggleStats } from "../actions";
|
||||
import { trackEvent } from "../analytics";
|
||||
import { isHandToolActive } from "../appState";
|
||||
import { TunnelsContext, useInitializeTunnels } from "../context/tunnels";
|
||||
import { UIAppStateContext } from "../context/ui-appState";
|
||||
import { useAtom, useAtomValue } from "../editor-jotai";
|
||||
@@ -55,7 +54,6 @@ import ElementLinkDialog from "./ElementLinkDialog";
|
||||
import { ErrorDialog } from "./ErrorDialog";
|
||||
import { EyeDropper, activeEyeDropperAtom } from "./EyeDropper";
|
||||
import { FixedSideContainer } from "./FixedSideContainer";
|
||||
import { HandButton } from "./HandButton";
|
||||
import { HelpDialog } from "./HelpDialog";
|
||||
import { HintViewer } from "./HintViewer";
|
||||
import { ImageExportDialog } from "./ImageExportDialog";
|
||||
@@ -359,13 +357,6 @@ const LayerUI = ({
|
||||
|
||||
<div className="App-toolbar__divider" />
|
||||
|
||||
<HandButton
|
||||
checked={isHandToolActive(appState)}
|
||||
onChange={() => onHandToolToggle()}
|
||||
title={t("toolBar.hand")}
|
||||
isMobile
|
||||
/>
|
||||
|
||||
<ShapesSwitcher
|
||||
setAppState={setAppState}
|
||||
activeTool={appState.activeTool}
|
||||
@@ -565,13 +556,13 @@ const LayerUI = ({
|
||||
<tunnels.OverwriteConfirmDialogTunnel.Out />
|
||||
{renderImageExportDialog()}
|
||||
{renderJSONExportDialog()}
|
||||
{appState.pasteDialog.shown && (
|
||||
{appState.openDialog?.name === "charts" && (
|
||||
<PasteChartDialog
|
||||
setAppState={setAppState}
|
||||
appState={appState}
|
||||
data={appState.openDialog.data}
|
||||
rawText={appState.openDialog.rawText}
|
||||
onClose={() =>
|
||||
setAppState({
|
||||
pasteDialog: { shown: false, data: null },
|
||||
openDialog: null,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -2,6 +2,40 @@
|
||||
|
||||
.excalidraw {
|
||||
.PasteChartDialog {
|
||||
.PasteChartDialog__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.PasteChartDialog__titleText {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.PasteChartDialog__reshuffleBtn {
|
||||
margin-left: auto;
|
||||
flex: 0 0 auto;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: var(--text-primary-color);
|
||||
transition: transform 120ms ease, background-color 120ms ease,
|
||||
color 120ms ease;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
color: $color-blue-6;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.94);
|
||||
}
|
||||
}
|
||||
|
||||
@include isMobile {
|
||||
.Island {
|
||||
display: flex;
|
||||
@@ -11,35 +45,61 @@
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
@include isMobile {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
.ChartPreview {
|
||||
margin: 8px;
|
||||
text-align: center;
|
||||
width: 192px;
|
||||
height: 128px;
|
||||
border-radius: 2px;
|
||||
padding: 1px;
|
||||
width: 260px;
|
||||
min-height: 190px;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
border: 1px solid $color-gray-4;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
justify-content: flex-start;
|
||||
gap: 10px;
|
||||
background: transparent;
|
||||
div {
|
||||
display: inline-block;
|
||||
.ChartPreview__canvas {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ChartPreview__label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
color: var(--text-primary-color);
|
||||
}
|
||||
svg {
|
||||
max-height: 120px;
|
||||
max-width: 186px;
|
||||
max-height: 144px;
|
||||
max-width: 100%;
|
||||
}
|
||||
&:hover {
|
||||
padding: 0;
|
||||
border: 2px solid $color-blue-5;
|
||||
border-color: $color-blue-5;
|
||||
}
|
||||
&:active {
|
||||
border-color: $color-blue-5;
|
||||
box-shadow: 0 0 0 1px $color-blue-5;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
&:focus-visible {
|
||||
border-color: $color-blue-5;
|
||||
box-shadow: 0 0 0 1px $color-blue-5;
|
||||
}
|
||||
|
||||
@include isMobile {
|
||||
width: 100%;
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,57 @@
|
||||
import React, { useLayoutEffect, useRef, useState } from "react";
|
||||
|
||||
import { newTextElement } from "@excalidraw/element";
|
||||
|
||||
import type { ChartType } from "@excalidraw/element/types";
|
||||
|
||||
import { trackEvent } from "../analytics";
|
||||
import { renderSpreadsheet } from "../charts";
|
||||
import { isSpreadsheetValidForChartType, renderSpreadsheet } from "../charts";
|
||||
import { t } from "../i18n";
|
||||
import { exportToSvg } from "../scene/export";
|
||||
|
||||
import { useUIAppState } from "../context/ui-appState";
|
||||
|
||||
import { useApp } from "./App";
|
||||
import { Dialog } from "./Dialog";
|
||||
|
||||
import "./PasteChartDialog.scss";
|
||||
|
||||
import { bucketFillIcon } from "./icons";
|
||||
|
||||
import type { ChartElements, Spreadsheet } from "../charts";
|
||||
import type { UIAppState } from "../types";
|
||||
|
||||
type OnPlainTextPaste = (rawText: string) => void;
|
||||
|
||||
type OnInsertChart = (chartType: ChartType, elements: ChartElements) => void;
|
||||
|
||||
const getChartTypeLabel = (chartType: ChartType) => {
|
||||
switch (chartType) {
|
||||
case "bar":
|
||||
return t("labels.chartType_bar");
|
||||
case "line":
|
||||
return t("labels.chartType_line");
|
||||
case "radar":
|
||||
return t("labels.chartType_radar");
|
||||
default:
|
||||
return chartType;
|
||||
}
|
||||
};
|
||||
|
||||
const ChartPreviewBtn = (props: {
|
||||
spreadsheet: Spreadsheet | null;
|
||||
chartType: ChartType;
|
||||
selected: boolean;
|
||||
colorSeed: number;
|
||||
onClick: OnInsertChart;
|
||||
}) => {
|
||||
const previewRef = useRef<HTMLDivElement | null>(null);
|
||||
const [chartElements, setChartElements] = useState<ChartElements | null>(
|
||||
null,
|
||||
);
|
||||
const { theme } = useUIAppState();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!props.spreadsheet) {
|
||||
setChartElements(null);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -38,7 +60,13 @@ const ChartPreviewBtn = (props: {
|
||||
props.spreadsheet,
|
||||
0,
|
||||
0,
|
||||
props.colorSeed,
|
||||
);
|
||||
if (!elements) {
|
||||
setChartElements(null);
|
||||
previewRef.current?.replaceChildren();
|
||||
return;
|
||||
}
|
||||
setChartElements(elements);
|
||||
let svg: SVGSVGElement;
|
||||
const previewNode = previewRef.current!;
|
||||
@@ -49,6 +77,7 @@ const ChartPreviewBtn = (props: {
|
||||
{
|
||||
exportBackground: false,
|
||||
viewBackgroundColor: "#fff",
|
||||
exportWithDarkMode: theme === "dark",
|
||||
},
|
||||
null, // files
|
||||
{
|
||||
@@ -58,42 +87,108 @@ const ChartPreviewBtn = (props: {
|
||||
svg.querySelector(".style-fonts")?.remove();
|
||||
previewNode.replaceChildren();
|
||||
previewNode.appendChild(svg);
|
||||
|
||||
if (props.selected) {
|
||||
(previewNode.parentNode as HTMLDivElement).focus();
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
previewNode.replaceChildren();
|
||||
};
|
||||
}, [props.spreadsheet, props.chartType, props.selected]);
|
||||
}, [props.spreadsheet, props.chartType, props.colorSeed, theme]);
|
||||
|
||||
const chartTypeLabel = getChartTypeLabel(props.chartType);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="ChartPreview"
|
||||
aria-label={chartTypeLabel}
|
||||
onClick={() => {
|
||||
if (chartElements) {
|
||||
props.onClick(props.chartType, chartElements);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div ref={previewRef} />
|
||||
<div className="ChartPreview__canvas" ref={previewRef} />
|
||||
<div className="ChartPreview__label">{chartTypeLabel}</div>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const PlainTextPreviewBtn = (props: {
|
||||
rawText: string;
|
||||
onClick: OnPlainTextPaste;
|
||||
}) => {
|
||||
const previewRef = useRef<HTMLDivElement | null>(null);
|
||||
const { theme } = useUIAppState();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!props.rawText) {
|
||||
return;
|
||||
}
|
||||
|
||||
const textElement = newTextElement({
|
||||
text: props.rawText,
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
const previewNode = previewRef.current!;
|
||||
|
||||
(async () => {
|
||||
const svg = await exportToSvg(
|
||||
[textElement],
|
||||
{
|
||||
exportBackground: false,
|
||||
viewBackgroundColor: "#fff",
|
||||
exportWithDarkMode: theme === "dark",
|
||||
},
|
||||
null,
|
||||
{
|
||||
skipInliningFonts: true,
|
||||
},
|
||||
);
|
||||
svg.querySelector(".style-fonts")?.remove();
|
||||
previewNode.replaceChildren();
|
||||
previewNode.appendChild(svg);
|
||||
})();
|
||||
|
||||
return () => {
|
||||
previewNode.replaceChildren();
|
||||
};
|
||||
}, [props.rawText, theme]);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="ChartPreview"
|
||||
aria-label={t("labels.chartType_plaintext")}
|
||||
onClick={() => {
|
||||
props.onClick(props.rawText);
|
||||
}}
|
||||
>
|
||||
<div className="ChartPreview__canvas" ref={previewRef} />
|
||||
<div className="ChartPreview__label">
|
||||
{t("labels.chartType_plaintext")}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export const PasteChartDialog = ({
|
||||
setAppState,
|
||||
appState,
|
||||
data,
|
||||
rawText,
|
||||
onClose,
|
||||
}: {
|
||||
appState: UIAppState;
|
||||
data: Spreadsheet;
|
||||
rawText: string;
|
||||
onClose: () => void;
|
||||
setAppState: React.Component<any, UIAppState>["setState"];
|
||||
}) => {
|
||||
const { onInsertElements } = useApp();
|
||||
const { onInsertElements, focusContainer } = useApp();
|
||||
const [colorSeed, setColorSeed] = useState(Math.random());
|
||||
|
||||
const handleReshuffleColors = React.useCallback(() => {
|
||||
setColorSeed(Math.random());
|
||||
}, []);
|
||||
|
||||
const handleClose = React.useCallback(() => {
|
||||
if (onClose) {
|
||||
onClose();
|
||||
@@ -103,36 +198,72 @@ export const PasteChartDialog = ({
|
||||
const handleChartClick = (chartType: ChartType, elements: ChartElements) => {
|
||||
onInsertElements(elements);
|
||||
trackEvent("paste", "chart", chartType);
|
||||
setAppState({
|
||||
currentChartType: chartType,
|
||||
pasteDialog: {
|
||||
shown: false,
|
||||
data: null,
|
||||
},
|
||||
onClose();
|
||||
focusContainer();
|
||||
};
|
||||
|
||||
const handlePlainTextClick = (rawText: string) => {
|
||||
const textElement = newTextElement({
|
||||
text: rawText,
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
onInsertElements([textElement]);
|
||||
trackEvent("paste", "chart", "plaintext");
|
||||
onClose();
|
||||
focusContainer();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
size="small"
|
||||
size="regular"
|
||||
onCloseRequest={handleClose}
|
||||
title={t("labels.pasteCharts")}
|
||||
title={
|
||||
<div className="PasteChartDialog__title">
|
||||
<div className="PasteChartDialog__titleText">
|
||||
{t("labels.pasteCharts")}
|
||||
</div>
|
||||
<div
|
||||
className="PasteChartDialog__reshuffleBtn"
|
||||
onClick={handleReshuffleColors}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter" || event.key === " ") {
|
||||
event.preventDefault();
|
||||
handleReshuffleColors();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{bucketFillIcon}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
className={"PasteChartDialog"}
|
||||
autofocus={false}
|
||||
>
|
||||
<div className={"container"}>
|
||||
<ChartPreviewBtn
|
||||
chartType="bar"
|
||||
spreadsheet={appState.pasteDialog.data}
|
||||
selected={appState.currentChartType === "bar"}
|
||||
onClick={handleChartClick}
|
||||
/>
|
||||
<ChartPreviewBtn
|
||||
chartType="line"
|
||||
spreadsheet={appState.pasteDialog.data}
|
||||
selected={appState.currentChartType === "line"}
|
||||
onClick={handleChartClick}
|
||||
/>
|
||||
{(["bar", "line", "radar"] as const).map((chartType) => {
|
||||
if (!isSpreadsheetValidForChartType(data, chartType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ChartPreviewBtn
|
||||
key={chartType}
|
||||
chartType={chartType}
|
||||
spreadsheet={data}
|
||||
colorSeed={colorSeed}
|
||||
onClick={handleChartClick}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{rawText && (
|
||||
<PlainTextPreviewBtn
|
||||
rawText={rawText}
|
||||
onClick={handlePlainTextClick}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -202,9 +202,11 @@ const InteractiveCanvas = (props: InteractiveCanvasProps) => {
|
||||
style={{
|
||||
width: props.appState.width,
|
||||
height: props.appState.height,
|
||||
cursor: props.appState.viewModeEnabled
|
||||
? CURSOR_TYPE.GRAB
|
||||
: CURSOR_TYPE.AUTO,
|
||||
cursor:
|
||||
props.appState.viewModeEnabled &&
|
||||
props.appState.activeTool.type !== "laser"
|
||||
? CURSOR_TYPE.GRAB
|
||||
: CURSOR_TYPE.AUTO,
|
||||
}}
|
||||
width={props.appState.width * props.scale}
|
||||
height={props.appState.height * props.scale}
|
||||
@@ -233,6 +235,7 @@ const getRelevantAppStateProps = (
|
||||
width: appState.width,
|
||||
height: appState.height,
|
||||
viewModeEnabled: appState.viewModeEnabled,
|
||||
activeTool: appState.activeTool,
|
||||
openDialog: appState.openDialog,
|
||||
editingGroupId: appState.editingGroupId,
|
||||
selectedElementIds: appState.selectedElementIds,
|
||||
@@ -246,6 +249,7 @@ const getRelevantAppStateProps = (
|
||||
multiElement: appState.multiElement,
|
||||
newElement: appState.newElement,
|
||||
isBindingEnabled: appState.isBindingEnabled,
|
||||
isMidpointSnappingEnabled: appState.isMidpointSnappingEnabled,
|
||||
suggestedBinding: appState.suggestedBinding,
|
||||
isRotating: appState.isRotating,
|
||||
elementsToHighlight: appState.elementsToHighlight,
|
||||
@@ -262,6 +266,7 @@ const getRelevantAppStateProps = (
|
||||
frameRendering: appState.frameRendering,
|
||||
shouldCacheIgnoreZoom: appState.shouldCacheIgnoreZoom,
|
||||
exportScale: appState.exportScale,
|
||||
currentItemArrowType: appState.currentItemArrowType,
|
||||
});
|
||||
|
||||
const areEqual = (
|
||||
|
||||
@@ -181,6 +181,21 @@
|
||||
box-shadow: 0 0 0 1px var(--color-brand-active);
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
|
||||
@@ -9,7 +9,9 @@ import {
|
||||
actionLoadScene,
|
||||
actionSaveToActiveFile,
|
||||
actionShortcuts,
|
||||
actionToggleArrowBinding,
|
||||
actionToggleGridMode,
|
||||
actionToggleMidpointSnapping,
|
||||
actionToggleObjectsSnapMode,
|
||||
actionToggleSearchMenu,
|
||||
actionToggleStats,
|
||||
@@ -443,6 +445,41 @@ const PreferencesToggleSnapModeItem = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const PreferencesToggleArrowBindingItem = () => {
|
||||
const { t } = useI18n();
|
||||
const actionManager = useExcalidrawActionManager();
|
||||
const appState = useUIAppState();
|
||||
return (
|
||||
<DropdownMenuItemCheckbox
|
||||
checked={appState.bindingPreference === "enabled"}
|
||||
onSelect={(event) => {
|
||||
actionManager.executeAction(actionToggleArrowBinding);
|
||||
event.preventDefault();
|
||||
}}
|
||||
>
|
||||
{t("labels.arrowBinding")}
|
||||
</DropdownMenuItemCheckbox>
|
||||
);
|
||||
};
|
||||
|
||||
const PreferencesToggleMidpointSnappingItem = () => {
|
||||
const { t } = useI18n();
|
||||
const actionManager = useExcalidrawActionManager();
|
||||
const appState = useUIAppState();
|
||||
return (
|
||||
<DropdownMenuItemCheckbox
|
||||
checked={appState.isMidpointSnappingEnabled}
|
||||
disabled={appState.bindingPreference === "disabled"}
|
||||
onSelect={(event) => {
|
||||
actionManager.executeAction(actionToggleMidpointSnapping);
|
||||
event.preventDefault();
|
||||
}}
|
||||
>
|
||||
{t("labels.midpointSnapping")}
|
||||
</DropdownMenuItemCheckbox>
|
||||
);
|
||||
};
|
||||
|
||||
export const PreferencesToggleGridModeItem = () => {
|
||||
const { t } = useI18n();
|
||||
const actionManager = useExcalidrawActionManager();
|
||||
@@ -538,6 +575,8 @@ export const Preferences = ({
|
||||
<PreferencesToggleZenModeItem />
|
||||
<PreferencesToggleViewModeItem />
|
||||
<PreferencesToggleElementPropertiesItem />
|
||||
<PreferencesToggleArrowBindingItem />
|
||||
<PreferencesToggleMidpointSnappingItem />
|
||||
</>
|
||||
)}
|
||||
{additionalItems}
|
||||
@@ -548,6 +587,8 @@ export const Preferences = ({
|
||||
|
||||
Preferences.ToggleToolLock = PreferencesToggleToolLockItem;
|
||||
Preferences.ToggleSnapMode = PreferencesToggleSnapModeItem;
|
||||
Preferences.ToggleArrowBinding = PreferencesToggleArrowBindingItem;
|
||||
Preferences.ToggleMidpointSnapping = PreferencesToggleMidpointSnappingItem;
|
||||
Preferences.ToggleGridMode = PreferencesToggleGridModeItem;
|
||||
Preferences.ToggleZenMode = PreferencesToggleZenModeItem;
|
||||
Preferences.ToggleViewMode = PreferencesToggleViewModeItem;
|
||||
|
||||
@@ -11,17 +11,28 @@ import {
|
||||
TextIcon,
|
||||
ImageIcon,
|
||||
EraserIcon,
|
||||
laserPointerToolIcon,
|
||||
handIcon,
|
||||
} from "./icons";
|
||||
|
||||
import type { AppClassProperties } from "../types";
|
||||
|
||||
export const SHAPES = [
|
||||
{
|
||||
icon: handIcon,
|
||||
value: "hand",
|
||||
key: KEYS.H,
|
||||
numericKey: null,
|
||||
fillable: false,
|
||||
toolbar: true,
|
||||
},
|
||||
{
|
||||
icon: SelectionIcon,
|
||||
value: "selection",
|
||||
key: KEYS.V,
|
||||
numericKey: KEYS["1"],
|
||||
fillable: true,
|
||||
toolbar: true,
|
||||
},
|
||||
{
|
||||
icon: RectangleIcon,
|
||||
@@ -29,6 +40,7 @@ export const SHAPES = [
|
||||
key: KEYS.R,
|
||||
numericKey: KEYS["2"],
|
||||
fillable: true,
|
||||
toolbar: true,
|
||||
},
|
||||
{
|
||||
icon: DiamondIcon,
|
||||
@@ -36,6 +48,7 @@ export const SHAPES = [
|
||||
key: KEYS.D,
|
||||
numericKey: KEYS["3"],
|
||||
fillable: true,
|
||||
toolbar: true,
|
||||
},
|
||||
{
|
||||
icon: EllipseIcon,
|
||||
@@ -43,6 +56,7 @@ export const SHAPES = [
|
||||
key: KEYS.O,
|
||||
numericKey: KEYS["4"],
|
||||
fillable: true,
|
||||
toolbar: true,
|
||||
},
|
||||
{
|
||||
icon: ArrowIcon,
|
||||
@@ -50,6 +64,7 @@ export const SHAPES = [
|
||||
key: KEYS.A,
|
||||
numericKey: KEYS["5"],
|
||||
fillable: true,
|
||||
toolbar: true,
|
||||
},
|
||||
{
|
||||
icon: LineIcon,
|
||||
@@ -57,6 +72,7 @@ export const SHAPES = [
|
||||
key: KEYS.L,
|
||||
numericKey: KEYS["6"],
|
||||
fillable: true,
|
||||
toolbar: true,
|
||||
},
|
||||
{
|
||||
icon: FreedrawIcon,
|
||||
@@ -64,6 +80,7 @@ export const SHAPES = [
|
||||
key: [KEYS.P, KEYS.X],
|
||||
numericKey: KEYS["7"],
|
||||
fillable: false,
|
||||
toolbar: true,
|
||||
},
|
||||
{
|
||||
icon: TextIcon,
|
||||
@@ -71,6 +88,7 @@ export const SHAPES = [
|
||||
key: KEYS.T,
|
||||
numericKey: KEYS["8"],
|
||||
fillable: false,
|
||||
toolbar: true,
|
||||
},
|
||||
{
|
||||
icon: ImageIcon,
|
||||
@@ -78,6 +96,7 @@ export const SHAPES = [
|
||||
key: null,
|
||||
numericKey: KEYS["9"],
|
||||
fillable: false,
|
||||
toolbar: true,
|
||||
},
|
||||
{
|
||||
icon: EraserIcon,
|
||||
@@ -85,6 +104,15 @@ export const SHAPES = [
|
||||
key: KEYS.E,
|
||||
numericKey: KEYS["0"],
|
||||
fillable: false,
|
||||
toolbar: true,
|
||||
},
|
||||
{
|
||||
icon: laserPointerToolIcon,
|
||||
value: "laser",
|
||||
key: KEYS.K,
|
||||
numericKey: null,
|
||||
fillable: false,
|
||||
toolbar: false,
|
||||
},
|
||||
] as const;
|
||||
|
||||
@@ -97,6 +125,7 @@ export const getToolbarTools = (app: AppClassProperties) => {
|
||||
key: KEYS.V,
|
||||
numericKey: KEYS["1"],
|
||||
fillable: true,
|
||||
toolbar: true,
|
||||
},
|
||||
...SHAPES.slice(1),
|
||||
] as const)
|
||||
|
||||
@@ -155,7 +155,7 @@ const repairBinding = <T extends ExcalidrawArrowElement>(
|
||||
| ExcalidrawElbowArrowElement["startBinding"]
|
||||
| ExcalidrawElbowArrowElement["endBinding"] = {
|
||||
...binding,
|
||||
fixedPoint: normalizeFixedPoint(binding.fixedPoint ?? [0, 0]),
|
||||
fixedPoint: normalizeFixedPoint(binding.fixedPoint),
|
||||
mode: binding.mode || "orbit",
|
||||
};
|
||||
|
||||
@@ -176,7 +176,7 @@ const repairBinding = <T extends ExcalidrawArrowElement>(
|
||||
return {
|
||||
elementId: binding.elementId,
|
||||
mode: binding.mode,
|
||||
fixedPoint: normalizeFixedPoint(binding.fixedPoint || [0.5, 0.5]),
|
||||
fixedPoint: normalizeFixedPoint(binding.fixedPoint),
|
||||
} as FixedPointBinding | null;
|
||||
}
|
||||
return null;
|
||||
@@ -185,15 +185,14 @@ const repairBinding = <T extends ExcalidrawArrowElement>(
|
||||
// binding schema v1 (legacy) -> attempt to migrate to v2
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const targetBoundElement =
|
||||
(targetElementsMap.get(binding.elementId) as ExcalidrawBindableElement) ||
|
||||
undefined;
|
||||
const targetBoundElement = targetElementsMap.get(binding.elementId) as
|
||||
| ExcalidrawBindableElement
|
||||
| undefined;
|
||||
const boundElement =
|
||||
targetBoundElement ||
|
||||
(existingElementsMap?.get(
|
||||
binding.elementId,
|
||||
) as ExcalidrawBindableElement) ||
|
||||
undefined;
|
||||
(existingElementsMap?.get(binding.elementId) as
|
||||
| ExcalidrawBindableElement
|
||||
| undefined);
|
||||
const elementsMap = targetBoundElement
|
||||
? targetElementsMap
|
||||
: existingElementsMap;
|
||||
@@ -208,11 +207,28 @@ const repairBinding = <T extends ExcalidrawArrowElement>(
|
||||
const mode = isPointInElement(p, boundElement, elementsMap)
|
||||
? "inside"
|
||||
: "orbit";
|
||||
const safeElement = {
|
||||
...element,
|
||||
startBinding: element.startBinding?.elementId
|
||||
? {
|
||||
...element.startBinding,
|
||||
mode,
|
||||
fixedPoint: normalizeFixedPoint(element.startBinding.fixedPoint),
|
||||
}
|
||||
: null,
|
||||
endBinding: element.endBinding?.elementId
|
||||
? {
|
||||
...element.endBinding,
|
||||
mode,
|
||||
fixedPoint: normalizeFixedPoint(element.endBinding.fixedPoint),
|
||||
}
|
||||
: null,
|
||||
};
|
||||
const focusPoint =
|
||||
mode === "inside"
|
||||
? p
|
||||
: projectFixedPointOntoDiagonal(
|
||||
element,
|
||||
safeElement,
|
||||
p,
|
||||
boundElement,
|
||||
startOrEnd,
|
||||
@@ -220,7 +236,7 @@ const repairBinding = <T extends ExcalidrawArrowElement>(
|
||||
{ value: 1 as NormalizedZoomValue },
|
||||
) || p;
|
||||
const { fixedPoint } = calculateFixedPointForNonElbowArrowBinding(
|
||||
element,
|
||||
safeElement,
|
||||
boundElement,
|
||||
startOrEnd,
|
||||
elementsMap,
|
||||
|
||||
@@ -319,3 +319,9 @@ export { isElementLink } from "@excalidraw/element";
|
||||
export { setCustomTextMetricsProvider } from "@excalidraw/element";
|
||||
|
||||
export { CommandPalette } from "./components/CommandPalette/CommandPalette";
|
||||
|
||||
export {
|
||||
renderSpreadsheet,
|
||||
tryParseSpreadsheet,
|
||||
isSpreadsheetValidForChartType,
|
||||
} from "./charts";
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "لصق",
|
||||
"pasteAsPlaintext": "اللصق كنص عادي",
|
||||
"pasteCharts": "لصق الرسوم البيانية",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "تحديد الكل",
|
||||
"multiSelect": "إضافة عنصر للتحديد",
|
||||
"moveCanvas": "نقل لوح الرسم",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Crow's foot (many)",
|
||||
"arrowhead_crowfoot_one": "Crow's foot (one)",
|
||||
"arrowhead_crowfoot_one_or_many": "Crow's foot (one or many)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "خيارات إضافية",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "نوع السهم",
|
||||
"arrowtype_sharp": "سهم حاد",
|
||||
"arrowtype_round": "سهم منحني",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "رابط إلى الكائن",
|
||||
"wrapSelectionInFrame": "تغليف التحديد في إطار",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "رابط إلى الكائن",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "خطأ"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "حفظ الملف على الجهاز",
|
||||
"disk_details": "تصدير بيانات المشهد إلى ملف يمكنك الاستيراد منه لاحقًا.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid إلى Excalidraw",
|
||||
"button": "إدراج",
|
||||
"description": "حاليًا، يتم دعم <flowchartLink>مخططات التدفق</flowchartLink>، <sequenceLink>التسلسلات</sequenceLink>، و<classLink>الفئات</classLink> فقط. سيتم عرض الأنواع الأخرى كصورة في Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "صيغة Mermaid",
|
||||
"preview": "معاينة",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Yapışdır",
|
||||
"pasteAsPlaintext": "Düz mətn kimi yapışdırın",
|
||||
"pasteCharts": "Diaqramları yapışdırın",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Hamısını seç",
|
||||
"multiSelect": "Seçimə element əlavə edin",
|
||||
"moveCanvas": "Kanvası köçürün",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": ""
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "",
|
||||
"disk_details": "",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Постави",
|
||||
"pasteAsPlaintext": "Постави като обикновен текст",
|
||||
"pasteCharts": "Постави графики",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Маркирай всичко",
|
||||
"multiSelect": "Добави елемент към селекция",
|
||||
"moveCanvas": "Премести платно",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Вид стрелка",
|
||||
"arrowtype_sharp": "Остра стрелка",
|
||||
"arrowtype_round": "Извита стрелка",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Грешка"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Запази към диск",
|
||||
"disk_details": "",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "Mermaid Синтаксис",
|
||||
"preview": "Преглед",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "পেস্ট করুন",
|
||||
"pasteAsPlaintext": "প্লেইনটেক্সট হিসাবে পেস্ট করুন",
|
||||
"pasteCharts": "চার্ট পেস্ট করুন",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "সবটা সিলেক্ট করুন",
|
||||
"multiSelect": "একাধিক সিলেক্ট করুন",
|
||||
"moveCanvas": "ক্যানভাস সরান",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "ত্রুটি"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "",
|
||||
"disk_details": "",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "পেস্ট করুন",
|
||||
"pasteAsPlaintext": "প্লেইনটেক্সট হিসাবে পেস্ট করুন",
|
||||
"pasteCharts": "চার্ট পেস্ট করুন",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "সবটা সিলেক্ট করুন",
|
||||
"multiSelect": "একাধিক সিলেক্ট করুন",
|
||||
"moveCanvas": "ক্যানভাস সরান",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "ত্রুটি"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "",
|
||||
"disk_details": "",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Enganxa",
|
||||
"pasteAsPlaintext": "Enganxar com a text pla",
|
||||
"pasteCharts": "Enganxa els diagrames",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Selecciona-ho tot",
|
||||
"multiSelect": "Afegeix un element a la selecció",
|
||||
"moveCanvas": "Mou el llenç",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Potes de gall (moltes)",
|
||||
"arrowhead_crowfoot_one": "Potes de gall (una)",
|
||||
"arrowhead_crowfoot_one_or_many": "Potes de gall (una o moltes)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Més opcions",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Tipus de fletxa",
|
||||
"arrowtype_sharp": "Fletxa Esmolada",
|
||||
"arrowtype_round": "Fletxa corba",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Enllaç a l'objecte",
|
||||
"wrapSelectionInFrame": "Embolica la selecció en un marc",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Enllaç a l'objecte",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Error"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Desa al disc",
|
||||
"disk_details": "Exporta les dades de l'escena a un fitxer que després podreu importar.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid a Excalidraw",
|
||||
"button": "Inseriu",
|
||||
"description": "Actualment només s'admeten els diagrames <flowchartLink>Flowchart</flowchartLink>, <sequenceLink> Sequence, </sequenceLink> i <classLink> Class </classLink>. Els altres tipus es representaran com a imatge a Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Sintaxi de Mermaid",
|
||||
"preview": "Previsualització",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Vložit",
|
||||
"pasteAsPlaintext": "Vložit jako prostý text",
|
||||
"pasteCharts": "Vložit grafy",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Vybrat vše",
|
||||
"multiSelect": "Přidat prvek do výběru",
|
||||
"moveCanvas": "Posunout plátno",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Typ šipky",
|
||||
"arrowtype_sharp": "Ostrá šipka",
|
||||
"arrowtype_round": "Zakřivená šipka",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Odkaz na objekt",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Odkaz na objekt",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Chyba"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Uložit na disk",
|
||||
"disk_details": "Exportovat data scény do souboru, ze kterého můžete importovat později.",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "Mermaid syntaxe",
|
||||
"preview": "Náhled",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Indsæt",
|
||||
"pasteAsPlaintext": "Indsæt som klartekst",
|
||||
"pasteCharts": "Indsæt diagrammer",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Marker alle",
|
||||
"multiSelect": "Tilføj element til markering",
|
||||
"moveCanvas": "Flyt lærred",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Kragefod (mange)",
|
||||
"arrowhead_crowfoot_one": "Kragefod (én)",
|
||||
"arrowhead_crowfoot_one_or_many": "Kragefod (én eller mange)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Flere muligheder",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Pile type",
|
||||
"arrowtype_sharp": "Skarp pil",
|
||||
"arrowtype_round": "Buet pil",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Fejl"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Gem til disk",
|
||||
"disk_details": "",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Einfügen",
|
||||
"pasteAsPlaintext": "Als unformatierten Text einfügen",
|
||||
"pasteCharts": "Diagramme einfügen",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Alle auswählen",
|
||||
"multiSelect": "Element zur Auswahl hinzufügen",
|
||||
"moveCanvas": "Leinwand verschieben",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Krähenfuß (viele)",
|
||||
"arrowhead_crowfoot_one": "Krähenfuß (einer)",
|
||||
"arrowhead_crowfoot_one_or_many": "Krähenfuß (einer oder viele)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Weitere Optionen",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Pfeiltyp",
|
||||
"arrowtype_sharp": "Scharfer Pfeil",
|
||||
"arrowtype_round": "Gebogener Pfeil",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Link zum Objekt",
|
||||
"wrapSelectionInFrame": "Auswahl in Rahmen einbetten",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Link zum Objekt",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Fehler"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Auf Festplatte speichern",
|
||||
"disk_details": "Exportiere die Zeichnungsdaten in eine Datei, die Du später importieren kannst.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid zu Excalidraw",
|
||||
"button": "Einfügen",
|
||||
"description": "Derzeit werden nur <flowchartLink>Flussdiagramme</flowchartLink>, <sequenceLink>Sequenzdiagramme</sequenceLink> und <classLink>Klassendiagramme</classLink> unterstützt. Die anderen Typen werden als Bild in Excalidraw dargestellt.",
|
||||
"description": "",
|
||||
"syntax": "Mermaid-Syntax",
|
||||
"preview": "Vorschau",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Einfügen",
|
||||
"pasteAsPlaintext": "Als unformatierten Text einfügen",
|
||||
"pasteCharts": "Diagramme einfügen",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Alle auswählen",
|
||||
"multiSelect": "Element zur Auswahl hinzufügen",
|
||||
"moveCanvas": "Leinwand verschieben",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Krähenfuß (viele)",
|
||||
"arrowhead_crowfoot_one": "Krähenfuß (einer)",
|
||||
"arrowhead_crowfoot_one_or_many": "Krähenfuß (einer oder viele)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Weitere Optionen",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Pfeiltyp",
|
||||
"arrowtype_sharp": "Scharfer Pfeil",
|
||||
"arrowtype_round": "Gebogener Pfeil",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Link zum Objekt",
|
||||
"wrapSelectionInFrame": "Auswahl in Rahmen einbetten",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Link zum Objekt",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Fehler"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Auf Festplatte speichern",
|
||||
"disk_details": "Exportiere die Zeichnungsdaten in eine Datei, die Du später importieren kannst.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid zu Excalidraw",
|
||||
"button": "Einfügen",
|
||||
"description": "Derzeit werden nur <flowchartLink>Flussdiagramme</flowchartLink>, <sequenceLink>Sequenzdiagramme</sequenceLink> und <classLink>Klassendiagramme</classLink> unterstützt. Die anderen Typen werden als Bild in Excalidraw dargestellt.",
|
||||
"description": "",
|
||||
"syntax": "Mermaid-Syntax",
|
||||
"preview": "Vorschau",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Επικόλληση",
|
||||
"pasteAsPlaintext": "Επικόλληση ως απλό κείμενο",
|
||||
"pasteCharts": "Επικόλληση γραφημάτων",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Επιλογή όλων",
|
||||
"multiSelect": "Προσθέστε το στοιχείο στην επιλογή",
|
||||
"moveCanvas": "Μετακίνηση καμβά",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Τύπος βέλους",
|
||||
"arrowtype_sharp": "Αιχμηρό βέλος",
|
||||
"arrowtype_round": "Κυρτό βέλος",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Σφάλμα"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Αποθήκευση στο δίσκο",
|
||||
"disk_details": "Εξαγωγή δεδομένων σκηνής σε ένα αρχείο από το οποίο μπορείτε να εισάγετε αργότερα.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid σε Excalidraw",
|
||||
"button": "Εισαγωγή",
|
||||
"description": "Επί του παρόντος υποστηρίζονται μόνο Διαγράμματα <flowchartLink>Ροής</flowchartLink>,<sequenceLink> Ακολουθίας, </sequenceLink> και <classLink>Κλάσεων</classLink>. Οι άλλοι τύποι θα αποδοθούν ως εικόνα στο Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Σύνταξη Mermaid",
|
||||
"preview": "Προεπισκόπηση",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Paste",
|
||||
"pasteAsPlaintext": "Paste as plaintext",
|
||||
"pasteCharts": "Paste charts",
|
||||
"chartType_bar": "Bar chart",
|
||||
"chartType_line": "Line chart",
|
||||
"chartType_radar": "Radar chart",
|
||||
"chartType_plaintext": "Plain text",
|
||||
"selectAll": "Select all",
|
||||
"multiSelect": "Add element to selection",
|
||||
"moveCanvas": "Move canvas",
|
||||
@@ -173,7 +177,9 @@
|
||||
"tab": "Tab",
|
||||
"shapeSwitch": "Switch shape",
|
||||
"preferences": "Preferences",
|
||||
"preferences_toolLock": "Tool lock"
|
||||
"preferences_toolLock": "Tool lock",
|
||||
"arrowBinding": "Arrow binding",
|
||||
"midpointSnapping": "Snap to midpoints"
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Link to object",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Pegar",
|
||||
"pasteAsPlaintext": "Pegar como texto sin formato",
|
||||
"pasteCharts": "Pegar gráficos",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Seleccionar todo",
|
||||
"multiSelect": "Añadir elemento a la selección",
|
||||
"moveCanvas": "Mover el lienzo",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Pie de la corona (varios)",
|
||||
"arrowhead_crowfoot_one": "Pie de la corona (uno)",
|
||||
"arrowhead_crowfoot_one_or_many": "Pie de la corona (uno o varios)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Más opciones",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Tipo de flecha",
|
||||
"arrowtype_sharp": "Flecha Afilada",
|
||||
"arrowtype_round": "Flecha Curva",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Enlace al objeto",
|
||||
"wrapSelectionInFrame": "Ajustar la selección en el marco",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Enlace al objeto",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Error"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Guardar en disco",
|
||||
"disk_details": "Exportar los datos de la escena a un archivo desde el cual pueda importar más tarde.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid a Excalidraw",
|
||||
"button": "Insertar",
|
||||
"description": "Actualmente sólo estos tipos de <flowchartLink>diagrama de flujo</flowchartLink>,<sequenceLink> Secuencia, </sequenceLink> y <classLink>Clase </classLink> son soportados. Los otros tipos de diagramas se renderizarán como imagen en Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Sintaxis Mermaid",
|
||||
"preview": "Vista previa",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Itsatsi",
|
||||
"pasteAsPlaintext": "Itsatsi testu arrunt gisa",
|
||||
"pasteCharts": "Itsatsi grafikoak",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Hautatu dena",
|
||||
"multiSelect": "Gehitu elementua hautapenera",
|
||||
"moveCanvas": "Mugitu oihala",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Errorea"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Gorde diskoan",
|
||||
"disk_details": "Esportatu eszenaren datuak geroago inportatu ahal izango duzun fitxategi batan.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid-etik Excalidraw-ra",
|
||||
"button": "Txertatu",
|
||||
"description": "Momentu honetan <flowchartLink>Flowchart</flowchartLink>, <sequenceLink> Sequence, </sequenceLink> eta <classLink>Class </classLink>Diagramak onartzen dira. Beste motak irudi gisa errendatuko dira Excalidrawn.",
|
||||
"description": "",
|
||||
"syntax": "Mermaid sintaxia",
|
||||
"preview": "Aurrebista",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "جایگذاری",
|
||||
"pasteAsPlaintext": "جایگذاری به عنوان متن ساده",
|
||||
"pasteCharts": "جایگذاری نمودارها",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "انتخاب همه",
|
||||
"multiSelect": "یک ایتم به انتخاب شده ها اضافه کنید",
|
||||
"moveCanvas": "جابجایی بوم",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "پای کلاغی (بسیار)",
|
||||
"arrowhead_crowfoot_one": "پای کلاغی (یک)",
|
||||
"arrowhead_crowfoot_one_or_many": "پای کلاغی (یک یا بسیار)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "امکانات بیشتر",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "نوع پیکان",
|
||||
"arrowtype_sharp": "پیکان تیز",
|
||||
"arrowtype_round": "پیکان منحنی",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "لینک به آیتم",
|
||||
"wrapSelectionInFrame": "انتخاب را در قاب قرار دهید",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "لینک به آیتم",
|
||||
@@ -196,7 +211,7 @@
|
||||
"multipleResults": "نتایج",
|
||||
"placeholder": "جستجوی متن در بوم...",
|
||||
"frames": "",
|
||||
"texts": ""
|
||||
"texts": "متن"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "پاکسازی بوم نقاشی",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "خطا"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "ذخیره در دیسک",
|
||||
"disk_details": "داده های صحنه را به فایلی که بعداً می توانید از آن وارد کنید صادر کنید.",
|
||||
@@ -571,7 +590,7 @@
|
||||
}
|
||||
},
|
||||
"colorPicker": {
|
||||
"color": "",
|
||||
"color": "رنگ",
|
||||
"mostUsedCustomColors": "رنگ های بهتازگی بهکار گرفته شده",
|
||||
"colors": "رنگها",
|
||||
"shades": "جلوهها",
|
||||
@@ -612,14 +631,15 @@
|
||||
"mermaid": {
|
||||
"title": "مرمید به excalidraw",
|
||||
"button": "درج",
|
||||
"description": "فعلا فقط <flowchartLink> فلوچارت </flowchartLink> ، <sequenceLink> توالی </sequenceLink> و <classLink> کلاس </classLink> نمودارها پشتیبانی می شوند. انواع دیگر به صورت تصویر در Excalidraw ارائه خواهند شد.",
|
||||
"description": "",
|
||||
"syntax": "مرمید syntax",
|
||||
"preview": "پیشنمایش",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
"error": "خطا!"
|
||||
},
|
||||
"chat": {
|
||||
"inputPlaceholder": "",
|
||||
@@ -627,8 +647,8 @@
|
||||
"generating": "",
|
||||
"rateLimitRemaining": "",
|
||||
"role": {
|
||||
"user": "",
|
||||
"assistant": "",
|
||||
"user": "شما",
|
||||
"assistant": "دستیار هوش مصنوعی",
|
||||
"system": ""
|
||||
},
|
||||
"aiBeta": "",
|
||||
@@ -707,9 +727,9 @@
|
||||
"alt": "",
|
||||
"escape": "",
|
||||
"enter": "",
|
||||
"shift": "",
|
||||
"spacebar": "",
|
||||
"delete": "",
|
||||
"shift": "Shift",
|
||||
"spacebar": "فاصله",
|
||||
"delete": "حذف",
|
||||
"mmb": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Liitä",
|
||||
"pasteAsPlaintext": "Liitä pelkkänä tekstinä",
|
||||
"pasteCharts": "Liitä kaaviot",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Valitse kaikki",
|
||||
"multiSelect": "Lisää kohde valintaan",
|
||||
"moveCanvas": "Siirrä piirtoaluetta",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Virhe"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Tallenna levylle",
|
||||
"disk_details": "Vie työn tiedot tiedostoon, josta sen voi tuoda myöhemmin.",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "Esikatsele",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Coller",
|
||||
"pasteAsPlaintext": "Coller comme texte brut",
|
||||
"pasteCharts": "Coller les graphiques",
|
||||
"chartType_bar": "Graphique à barres",
|
||||
"chartType_line": "Courbes",
|
||||
"chartType_radar": "Diagramme en radar",
|
||||
"chartType_plaintext": "Texte brut",
|
||||
"selectAll": "Tout sélectionner",
|
||||
"multiSelect": "Ajouter l'élément à la sélection",
|
||||
"moveCanvas": "Déplacer le canevas",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "Pied de Corde (un)",
|
||||
"arrowhead_crowfoot_one_or_many": "Pied du corbeau (un ou plusieurs)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Plus d'options",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Type de flèche",
|
||||
"arrowtype_sharp": "Flèche pointue",
|
||||
"arrowtype_round": "Flèche incurvée",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Lien vers un objet",
|
||||
"wrapSelectionInFrame": "Mettre la sélection dans un cadre",
|
||||
"tab": "Onglet",
|
||||
"shapeSwitch": "Changer de forme"
|
||||
"shapeSwitch": "Changer de forme",
|
||||
"preferences": "Préférences",
|
||||
"preferences_toolLock": "Verrouillage de l'outil",
|
||||
"arrowBinding": "Liaison de flèches",
|
||||
"midpointSnapping": "S'accrocher aux points intermédiaires"
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Lien vers un objet",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Erreur"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Enregistrer sur le disque",
|
||||
"disk_details": "Exporter les données de la scène comme un fichier que vous pourrez importer ultérieurement.",
|
||||
@@ -557,9 +576,9 @@
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_line2": "",
|
||||
"center_heading_line3": "",
|
||||
"center_heading": "Vos dessins sont enregistrés dans le stockage de votre navigateur.",
|
||||
"center_heading_line2": "Le stockage du navigateur peut être effacé de manière inattendue.",
|
||||
"center_heading_line3": "Enregistrez régulièrement votre travail dans un fichier pour éviter de le perdre.",
|
||||
"center_heading_plus": "Vouliez-vous plutôt aller à Excalidraw+ à la place ?",
|
||||
"menuHint": "Exportation, préférences, langues, ..."
|
||||
},
|
||||
@@ -612,57 +631,58 @@
|
||||
"mermaid": {
|
||||
"title": "De Mermaid à Excalidraw",
|
||||
"button": "Insérer",
|
||||
"description": "Actuellement, seuls les diagrammes <flowchartLink>Flowchart</flowchartLink>,<sequenceLink> Sequence, </sequenceLink> et <classLink>de classe </classLink>sont pris en charge. Les autres types seront rendus en tant qu'image dans Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Syntaxe Mermaid",
|
||||
"preview": "Prévisualisation",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"label": "Mermaid",
|
||||
"inputPlaceholder": "Écrivez la définition du diagramme de Mermaid ici...",
|
||||
"autoFixAvailable": "Correction automatique disponible"
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
"error": "Erreur!"
|
||||
},
|
||||
"chat": {
|
||||
"inputPlaceholder": "",
|
||||
"inputPlaceholderWithMessages": "",
|
||||
"generating": "",
|
||||
"rateLimitRemaining": "",
|
||||
"inputPlaceholder": "Commencez à taper votre idée de diagramme ici... ({{shortcut}} pour une nouvelle ligne)",
|
||||
"inputPlaceholderWithMessages": "Continuer à affiner votre diagramme...",
|
||||
"generating": "Génération...",
|
||||
"rateLimitRemaining": "{{count}} demandes restantes aujourd'hui",
|
||||
"role": {
|
||||
"user": "",
|
||||
"assistant": "",
|
||||
"system": ""
|
||||
"user": "Vous",
|
||||
"assistant": "Assistant IA",
|
||||
"system": "Système"
|
||||
},
|
||||
"aiBeta": "",
|
||||
"label": "",
|
||||
"menu": "",
|
||||
"newChat": "",
|
||||
"deleteChat": "",
|
||||
"deleteMessage": "",
|
||||
"viewAsMermaid": "",
|
||||
"aiBeta": "Bêta IA",
|
||||
"label": "Chat",
|
||||
"menu": "Menu",
|
||||
"newChat": "Nouveau chat",
|
||||
"deleteChat": "Supprimer le chat",
|
||||
"deleteMessage": "Supprimer le message",
|
||||
"viewAsMermaid": "Voir en tant que Mermaid",
|
||||
"placeholder": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "Créons votre diagramme",
|
||||
"description": "Décrivez le diagramme que vous voulez créer, et nous le générerons pour vous.",
|
||||
"hint": ""
|
||||
},
|
||||
"preview": "",
|
||||
"insert": "",
|
||||
"retry": "",
|
||||
"preview": "Prévisualisation",
|
||||
"insert": "Insérer",
|
||||
"retry": "Recommencer",
|
||||
"errors": {
|
||||
"promptTooShort": "",
|
||||
"promptTooLong": "",
|
||||
"generationFailed": "",
|
||||
"invalidDiagram": "",
|
||||
"fixInMermaid": "",
|
||||
"aiRepair": "",
|
||||
"requestAborted": "",
|
||||
"requestFailed": "",
|
||||
"mermaidParseError": ""
|
||||
"promptTooShort": "La requête est trop courte (min {{min}} caractères)",
|
||||
"promptTooLong": "La requête est trop longue (max {{max}} caractères)",
|
||||
"generationFailed": "Échec de la génération",
|
||||
"invalidDiagram": "A généré un diagramme non valide :(. Vous pouvez modifier manuellement, réessayer avec la correction automatique, ou essayer une invite différente.",
|
||||
"fixInMermaid": "Modifier Mermaid manuellement →",
|
||||
"aiRepair": "Régénérer (correction automatique) →",
|
||||
"requestAborted": "Demande abandonnée",
|
||||
"requestFailed": "La requête a échoué",
|
||||
"mermaidParseError": "Erreur de syntaxe Mermaid"
|
||||
},
|
||||
"rateLimit": {
|
||||
"messageLimit": "",
|
||||
"generalRateLimit": "",
|
||||
"messageLimitInputPlaceholder": ""
|
||||
"messageLimit": "Vous avez atteint votre limite d'IA sur le plan gratuit. Essayez Excalidraw+ pour plus ou revenez demain.",
|
||||
"generalRateLimit": "Tenez vos chevaux, vous êtes trop rapide pour nous! Veuillez attendre un instant avant de réessayer.",
|
||||
"messageLimitInputPlaceholder": "Vous avez atteint votre limite de messages"
|
||||
},
|
||||
"upsellBtnLabel": ""
|
||||
"upsellBtnLabel": "Devenez Premium"
|
||||
},
|
||||
"quickSearch": {
|
||||
"placeholder": "Recherche rapide"
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Pegar",
|
||||
"pasteAsPlaintext": "Pegar coma texto sen formato",
|
||||
"pasteCharts": "Pegar gráficos",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Seleccionar todo",
|
||||
"multiSelect": "Engadir elemento á selección",
|
||||
"moveCanvas": "Mover o lenzo",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Erro"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Gardar no disco",
|
||||
"disk_details": "Exporte os datos da escena a un ficheiro que poderás importar máis tarde.",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "Vista previa",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "הדבקה",
|
||||
"pasteAsPlaintext": "הדבקה ללא עיצוב",
|
||||
"pasteCharts": "הדבקת תרשימים",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "בחירה בהכול",
|
||||
"multiSelect": "הוספת רכיב לבחירה",
|
||||
"moveCanvas": "הזזת הקנבס",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "רגל עורב (הרבה)",
|
||||
"arrowhead_crowfoot_one": "רגל עורב (אחת)",
|
||||
"arrowhead_crowfoot_one_or_many": "רגל עורב (אחת או הרבה)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "אפשרויות נוספות",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "סוג החץ",
|
||||
"arrowtype_sharp": "חץ מחודד",
|
||||
"arrowtype_round": "חץ מעוקל",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "קישור לפריט",
|
||||
"wrapSelectionInFrame": "לעטוף את הבחירה במסגרת",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "קישור לפריט",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "שגיאה"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "שמור לכונן",
|
||||
"disk_details": "ייצא מידע של הקנבאס לקובץ שתוכל לייבא אחר כך.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid ל־Excalidraw",
|
||||
"button": "הוספה",
|
||||
"description": "לעת עתה נתמכים רק <flowchartLink>תרשימי זרימה</flowchartLink>, <sequenceLink>תהליכים</sequenceLink>, <classLink>ודיאגרמת מחלקה</classLink>. שאר הסוגים ייוצרו כתמונות ב-Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "תחביר Mermaid",
|
||||
"preview": "תצוגה מקדימה",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "पेस्ट",
|
||||
"pasteAsPlaintext": "सादे पाठ के रूप में चिपकाएं",
|
||||
"pasteCharts": "चार्ट चिपकाएँ",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "सेलेक्ट ऑल",
|
||||
"multiSelect": "अवयव को चयन में सम्मिलित करें",
|
||||
"moveCanvas": "चित्रपटल को स्थानांतरित करें",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "चिड़िया पैर (अनेक)",
|
||||
"arrowhead_crowfoot_one": "चिड़िया पैर (एक)",
|
||||
"arrowhead_crowfoot_one_or_many": "चिड़िया पैर (एक या अनेक)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "और विकल्प",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "तीर प्रकार",
|
||||
"arrowtype_sharp": "तीक्ष्ण तीर",
|
||||
"arrowtype_round": "गोलाकार तीर",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "वस्तु की कड़ी",
|
||||
"wrapSelectionInFrame": "चौकट में चुने हुवे को लपेटे",
|
||||
"tab": "",
|
||||
"shapeSwitch": "आकार बदलें"
|
||||
"shapeSwitch": "आकार बदलें",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "वस्तु की कड़ी",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "त्रुटि"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "डिस्क पर सुरक्षित करे",
|
||||
"disk_details": "दृश्य डेटा एक फ़ाइल में निर्यात करे, जहांसे आप उसे पुनः आयात कर सके",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "मर्मेड से एक्सकाली में",
|
||||
"button": "सन्निवेश करे",
|
||||
"description": "वर्तमान में केवल <flowchartLink>बहाव चित्र</flowchartLink>, <sequenceLink> अनुक्रम चित्र </sequenceLink> और <classLink>वर्ग चित्र</classLink> का चित्रिकरण संभव हैं. अन्य चित्र प्रकार एक्सकाली प्रतिमा जैसे चित्रित किए जायेंगे.",
|
||||
"description": "",
|
||||
"syntax": "मर्मेड विन्यास",
|
||||
"preview": "पूर्वावलोकन",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Beillesztés",
|
||||
"pasteAsPlaintext": "Beillesztés formázatlan szövegként",
|
||||
"pasteCharts": "Grafikon beillesztése",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Összes kijelölése",
|
||||
"multiSelect": "Elem hozzáadása a kijelöléshez",
|
||||
"moveCanvas": "Vászon mozgatása",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Nyíl típus",
|
||||
"arrowtype_sharp": "Hegyes nyíl",
|
||||
"arrowtype_round": "Ívelt nyíl",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Hivatkozás az objektumhoz",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "Tab",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Hivatkozás az objektumhoz",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Hiba"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Mentés lemezre",
|
||||
"disk_details": "Exportálja a jelenetadatokat egy fájlba, amelyből később importálhatja.",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Tempel",
|
||||
"pasteAsPlaintext": "Tempel sebagai teks biasa",
|
||||
"pasteCharts": "Tempel diagram",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Pilih semua",
|
||||
"multiSelect": "Tambah unsur ke seleksi",
|
||||
"moveCanvas": "Pindahkan kanvas",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Pilihan lain",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Jenis panah",
|
||||
"arrowtype_sharp": "Panah runcing",
|
||||
"arrowtype_round": "Panah melengkung",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Taut ke objek",
|
||||
"wrapSelectionInFrame": "Bungkus pilihan dalam bingkai",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Taut ke objek",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Kesalahan"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Simpan ke disk",
|
||||
"disk_details": "Ekspor data pemandangan ke file yang mana Anda dapat impor nanti.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid menjadi Excalidraw",
|
||||
"button": "Sisipkan",
|
||||
"description": "Saat ini hanya <flowchartLink>Flowchart</flowchartLink>, <sequenceLink>Sekuen, </sequenceLink>, dan <classLink>Kelas</classLink>Diagram yang didukung. Jenis lainnya akan dirender sebagai gambar di Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Syntax Mermaid",
|
||||
"preview": "Pratinjau",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Incolla",
|
||||
"pasteAsPlaintext": "Incolla come testo normale",
|
||||
"pasteCharts": "Incolla grafici",
|
||||
"chartType_bar": "Grafico a barre",
|
||||
"chartType_line": "Grafico a linee",
|
||||
"chartType_radar": "Grafico radar",
|
||||
"chartType_plaintext": "Testo semplice",
|
||||
"selectAll": "Seleziona tutto",
|
||||
"multiSelect": "Aggiungi elemento alla selezione",
|
||||
"moveCanvas": "Sposta tela",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Zampe di gallina (molti)",
|
||||
"arrowhead_crowfoot_one": "Zampa di gallina (una)",
|
||||
"arrowhead_crowfoot_one_or_many": "Zampe di gallina (una o molte)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Altre opzioni",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Tipo di freccia",
|
||||
"arrowtype_sharp": "Freccia affilata",
|
||||
"arrowtype_round": "Freccia curva",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Link all'oggetto",
|
||||
"wrapSelectionInFrame": "Avvolgi la selezione nella cornice",
|
||||
"tab": "Scheda",
|
||||
"shapeSwitch": "Cambia forma"
|
||||
"shapeSwitch": "Cambia forma",
|
||||
"preferences": "Preferenze",
|
||||
"preferences_toolLock": "Blocco strumento",
|
||||
"arrowBinding": "Legatura a freccia",
|
||||
"midpointSnapping": "Aggancia ai punti medi"
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Link all'oggetto",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Errore"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Salva su disco",
|
||||
"disk_details": "Esporta i dati della scena su file, dal quale potrai importare in seguito.",
|
||||
@@ -557,9 +576,9 @@
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_line2": "",
|
||||
"center_heading_line3": "",
|
||||
"center_heading": "I tuoi disegni vengono salvati nella memoria del tuo browser.",
|
||||
"center_heading_line2": "La memoria del browser potrebbe essere cancellata inaspettatamente.",
|
||||
"center_heading_line3": "Salva regolarmente il tuo lavoro in un file per evitare di perderlo.",
|
||||
"center_heading_plus": "Volevi invece andare su Excalidraw+?",
|
||||
"menuHint": "Esporta, preferenze, lingue, ..."
|
||||
},
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Da Mermaid a Excalidraw",
|
||||
"button": "Inserisci",
|
||||
"description": "Attualmente sono supportati solo diagrammi di <flowchartLink>flusso</flowchartLink>,<sequenceLink> sequenza, </sequenceLink> e <classLink>classe </classLink>. Gli altri tipi saranno rappresentati come immagini in Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Sintassi Mermaid",
|
||||
"preview": "Anteprima",
|
||||
"label": "Mermaid",
|
||||
"inputPlaceholder": "Scrivi qui la definizione del diagramma Mermaid..."
|
||||
"inputPlaceholder": "Scrivi qui la definizione del diagramma Mermaid...",
|
||||
"autoFixAvailable": "Correzione automatica disponibile"
|
||||
},
|
||||
"ttd": {
|
||||
"error": "Errore!"
|
||||
@@ -641,7 +661,7 @@
|
||||
"placeholder": {
|
||||
"title": "Progettiamo il tuo diagramma",
|
||||
"description": "Descrivi il diagramma che vuoi creare e noi lo genereremo per te.",
|
||||
"hint": "Al momento conosciamo i diagrammi di flusso, di sequenza e di classe."
|
||||
"hint": ""
|
||||
},
|
||||
"preview": "Anteprima",
|
||||
"insert": "Inserisci",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "貼り付け",
|
||||
"pasteAsPlaintext": "書式なしテキストとして貼り付け",
|
||||
"pasteCharts": "チャートの貼り付け",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "すべて選択",
|
||||
"multiSelect": "複数選択",
|
||||
"moveCanvas": "キャンバスを移動",
|
||||
@@ -34,7 +38,7 @@
|
||||
"opacity": "透明度",
|
||||
"textAlign": "文字の配置",
|
||||
"edges": "角",
|
||||
"sharp": "四角",
|
||||
"sharp": "シャープ",
|
||||
"round": "丸",
|
||||
"arrowheads": "線の終点",
|
||||
"arrowhead_none": "なし",
|
||||
@@ -46,14 +50,21 @@
|
||||
"arrowhead_triangle_outline": "三角 (中抜き)",
|
||||
"arrowhead_diamond": "ひし形",
|
||||
"arrowhead_diamond_outline": "ひし形 (中抜き)",
|
||||
"arrowhead_crowfoot_many": "鳥の足記法(多対多)",
|
||||
"arrowhead_crowfoot_one": "鳥の足記法(一対一)",
|
||||
"arrowhead_crowfoot_one_or_many": "鳥の足記法(一対多)",
|
||||
"more_options": "詳細設定",
|
||||
"arrowtypes": "矢印の種類",
|
||||
"arrowhead_crowfoot_many": "カラスの足 (*)",
|
||||
"arrowhead_crowfoot_one": "カラスの足 (1)",
|
||||
"arrowhead_crowfoot_one_or_many": "カラスの足 (1..*)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "その他のオプション",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "矢印タイプ",
|
||||
"arrowtype_sharp": "鋭い矢印",
|
||||
"arrowtype_round": "曲線矢印",
|
||||
"arrowtype_elbowed": "ひじ矢印",
|
||||
"arrowtype_elbowed": "折れ線矢印",
|
||||
"fontSize": "フォントの大きさ",
|
||||
"fontFamily": "フォントの種類",
|
||||
"addWatermark": "\"Made with Excalidraw\"と表示",
|
||||
@@ -75,14 +86,14 @@
|
||||
"right": "右寄せ",
|
||||
"extraBold": "極太",
|
||||
"architect": "建築家",
|
||||
"artist": "アーティスト",
|
||||
"artist": "画家",
|
||||
"cartoonist": "漫画家",
|
||||
"fileTitle": "ファイル名",
|
||||
"colorPicker": "カラーピッカー",
|
||||
"canvasColors": "キャンバス上で使用",
|
||||
"canvasBackground": "キャンバスの背景",
|
||||
"drawingCanvas": "キャンバスの描画",
|
||||
"clearCanvas": "キャンバスを片付ける",
|
||||
"clearCanvas": "キャンバスを消去",
|
||||
"layers": "レイヤー",
|
||||
"actions": "操作",
|
||||
"language": "言語",
|
||||
@@ -101,7 +112,7 @@
|
||||
"libraryLoadingMessage": "ライブラリを読み込み中…",
|
||||
"libraries": "ライブラリを参照する",
|
||||
"loadingScene": "シーンを読み込み中…",
|
||||
"loadScene": "ファイルからシーン",
|
||||
"loadScene": "ファイルからシーンを開く",
|
||||
"align": "配置",
|
||||
"alignTop": "上揃え",
|
||||
"alignBottom": "下揃え",
|
||||
@@ -160,18 +171,22 @@
|
||||
"prompt": "プロンプト",
|
||||
"followUs": "フォローする",
|
||||
"discordChat": "Discord チャット",
|
||||
"zoomToFitViewport": "表寿範囲に合わせてズーム",
|
||||
"zoomToFitViewport": "表示範囲に合わせてズーム",
|
||||
"zoomToFitSelection": "選択範囲に合わせてズーム",
|
||||
"zoomToFit": "すべての要素が収まるようにズーム",
|
||||
"installPWA": "Excalidrawをローカルにインストール(PWA)",
|
||||
"autoResize": "テキストの自動サイズ変更を有効化",
|
||||
"imageCropping": "画像のトリミング",
|
||||
"unCroppedDimension": "",
|
||||
"copyElementLink": "",
|
||||
"imageCropping": "画像の切り抜き",
|
||||
"unCroppedDimension": "元のサイズ",
|
||||
"copyElementLink": "オブジェクトへのリンクをコピー",
|
||||
"linkToElement": "オブジェクトにリンク",
|
||||
"wrapSelectionInFrame": "選択範囲を枠で折り返す",
|
||||
"wrapSelectionInFrame": "選択範囲をフレームで囲む",
|
||||
"tab": "Tab",
|
||||
"shapeSwitch": "図形形状を変更"
|
||||
"shapeSwitch": "図形形状を変更",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "オブジェクトにリンク",
|
||||
@@ -185,15 +200,15 @@
|
||||
"search": {
|
||||
"inputPlaceholder": "ライブラリを検索",
|
||||
"heading": "ライブラリと一致",
|
||||
"noResults": "一致するアイテムが見つかりませんでした…",
|
||||
"noResults": "一致するアイテムはありません…",
|
||||
"clearSearch": "検索のクリア"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "キャンバスで検索",
|
||||
"noMatch": "一致なし…",
|
||||
"singleResult": "結果",
|
||||
"multipleResults": "結果数",
|
||||
"noMatch": "一致する結果はありません…",
|
||||
"singleResult": "件",
|
||||
"multipleResults": "件",
|
||||
"placeholder": "キャンバス内のテキストを検索…",
|
||||
"frames": "フレーム",
|
||||
"texts": "テキスト"
|
||||
@@ -240,7 +255,7 @@
|
||||
"embeddableInteractionButton": "クリックして操作"
|
||||
},
|
||||
"alerts": {
|
||||
"clearReset": "この操作によってキャンバス全体が消えます。よろしいですか?",
|
||||
"clearReset": "キャンバス全体を消去します。本当によろしいですか?",
|
||||
"couldNotCreateShareableLink": "共有URLを作成できませんでした。",
|
||||
"couldNotCreateShareableLinkTooBig": "共有可能なリンクを作成できませんでした: シーンが大きすぎます",
|
||||
"couldNotLoadInvalidFile": "無効なファイルを読み込めませんでした。",
|
||||
@@ -249,8 +264,8 @@
|
||||
"couldNotCopyToClipboard": "クリップボードにコピーできませんでした。",
|
||||
"decryptFailed": "データを復号できませんでした。",
|
||||
"uploadedSecurly": "データのアップロードはエンドツーエンド暗号化によって保護されています。Excalidrawサーバーと第三者はデータの内容を見ることができません。",
|
||||
"loadSceneOverridePrompt": "外部図面を読み込むと、既存のコンテンツが置き換わります。続行しますか?",
|
||||
"collabStopOverridePrompt": "セッションを停止すると、ローカルに保存されている図が上書きされます。 本当によろしいですか?\n\n(ローカルの図を保持したい場合は、セッションを停止せずにブラウザタブを閉じてください。)",
|
||||
"loadSceneOverridePrompt": "外部の描画データを読み込むと、既存のコンテンツが置き換わります。続行しますか?",
|
||||
"collabStopOverridePrompt": "セッションを停止すると、ローカルに保存されている図が上書きされます。 本当によろしいですか?\n\n(ローカルの図を保持したい場合は、セッションを停止せずにブラウザーのタブを閉じてください。)",
|
||||
"errorAddingToLibrary": "アイテムをライブラリに追加できませんでした",
|
||||
"errorRemovingFromLibrary": "ライブラリからアイテムを削除できませんでした",
|
||||
"confirmAddLibrary": "{{numShapes}} 個の図形をライブラリに追加します。よろしいですか?",
|
||||
@@ -261,7 +276,7 @@
|
||||
"removeItemsFromsLibrary": "{{count}} 個のアイテムをライブラリから削除しますか?",
|
||||
"invalidEncryptionKey": "暗号化キーは22文字でなければなりません。共同編集は無効化されています。",
|
||||
"collabOfflineWarning": "インターネットに接続されていません。\n変更は保存されません!",
|
||||
"localStorageQuotaExceeded": ""
|
||||
"localStorageQuotaExceeded": "ブラウザーのストレージ容量を超えました。変更は保存されません。"
|
||||
},
|
||||
"errors": {
|
||||
"unsupportedFileType": "サポートされていないファイル形式です。",
|
||||
@@ -271,13 +286,13 @@
|
||||
"failedToFetchImage": "画像の読み込みに失敗しました。",
|
||||
"cannotResolveCollabServer": "コラボレーションサーバに接続できませんでした。ページを再読み込みして、もう一度お試しください。",
|
||||
"importLibraryError": "ライブラリを読み込めませんでした。",
|
||||
"saveLibraryError": "",
|
||||
"saveLibraryError": "ライブラリをストレージに保存できませんでした。変更を失わないように、ローカルにファイルとして保存してください。",
|
||||
"collabSaveFailed": "バックエンドデータベースに保存できませんでした。問題が解決しない場合は、作業を失わないようにローカルにファイルを保存してください。",
|
||||
"collabSaveFailed_sizeExceeded": "キャンバスが大きすぎるため、バックエンドデータベースに保存できませんでした。問題が解決しない場合は、作業を失わないようにローカルにファイルを保存してください。",
|
||||
"imageToolNotSupported": "画像ツールは使用不可です。",
|
||||
"brave_measure_text_error": {
|
||||
"line1": "<bold>Aggressly Block Fingerprinting</bold> の設定が有効なBraveブラウザを使用しているようです。",
|
||||
"line2": "これにより、図面の <bold>テキスト要素</bold> が壊れる可能性があります。",
|
||||
"line1": "<bold>Aggressly Block Fingerprinting</bold> 設定が有効なBraveブラウザーを使用しているようです。",
|
||||
"line2": "これにより、描画内の<bold>テキスト要素</bold>が壊れる可能性があります。",
|
||||
"line3": "この設定を無効にすることを強く推奨します。 <link>設定手順</link> をこちらから確認できます。",
|
||||
"line4": "この設定を無効にすると、テキスト要素の表示が修正されません。 GitHub で <issueLink>Issue</issueLink> を開くか、 <discordLink>Discord</discordLink> にご記入ください"
|
||||
},
|
||||
@@ -292,9 +307,9 @@
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "選択",
|
||||
"lasso": "",
|
||||
"lasso": "なげなわ選択",
|
||||
"image": "画像を挿入",
|
||||
"rectangle": "矩形",
|
||||
"rectangle": "長方形",
|
||||
"diamond": "ひし形",
|
||||
"ellipse": "楕円",
|
||||
"arrow": "矢印",
|
||||
@@ -304,31 +319,31 @@
|
||||
"library": "ライブラリ",
|
||||
"lock": "描画後も使用中のツールを選択したままにする",
|
||||
"penMode": "ペンモード - タッチ防止",
|
||||
"link": "",
|
||||
"link": "選択した図形にリンクを追加・更新",
|
||||
"eraser": "消しゴム",
|
||||
"frame": "フレームツール",
|
||||
"frame": "フレーム",
|
||||
"magicframe": "ワイヤーフレームからコードを生成",
|
||||
"embeddable": "Web埋め込み",
|
||||
"laser": "レーザーポインター",
|
||||
"hand": "手 (パンニングツール)",
|
||||
"extraTools": "その他のツール",
|
||||
"mermaidToExcalidraw": "Mermaid を Excalidraw に変換",
|
||||
"convertElementType": ""
|
||||
"convertElementType": "図形タイプを切り替え"
|
||||
},
|
||||
"element": {
|
||||
"rectangle": "",
|
||||
"diamond": "",
|
||||
"rectangle": "長方形",
|
||||
"diamond": "ひし形",
|
||||
"ellipse": "楕円",
|
||||
"arrow": "",
|
||||
"line": "",
|
||||
"freedraw": "",
|
||||
"arrow": "矢印",
|
||||
"line": "線",
|
||||
"freedraw": "フリーハンド",
|
||||
"text": "文字",
|
||||
"image": "画像",
|
||||
"group": "グループ",
|
||||
"frame": "フレーム",
|
||||
"magicframe": "ワイヤーフレームからコードを生成",
|
||||
"embeddable": "",
|
||||
"selection": "",
|
||||
"embeddable": "Web埋め込み",
|
||||
"selection": "選択",
|
||||
"iframe": "IFrame"
|
||||
},
|
||||
"headings": {
|
||||
@@ -337,34 +352,34 @@
|
||||
"shapes": "図形"
|
||||
},
|
||||
"hints": {
|
||||
"dismissSearch": "",
|
||||
"canvasPanning": "",
|
||||
"dismissSearch": "{{shortcut}} で検索を閉じる",
|
||||
"canvasPanning": "キャンバスを移動するには、{{shortcut_1}} か {{shortcut_2}} を押しながらドラッグ、または手のひらツールを使用",
|
||||
"linearElement": "クリックすると複数の頂点からなる曲線を開始、ドラッグすると直線",
|
||||
"arrowTool": "",
|
||||
"arrowBindModifiers": "",
|
||||
"arrowTool": "クリックで複数の点を追加、ドラッグで直線。{{shortcut}} を再度押すと矢印タイプが変更されます。",
|
||||
"arrowBindModifiers": "{{shortcut_1}} を押し続けるとバインドを無効化、{{shortcut_2}} を押し続けると固定点にバインド",
|
||||
"freeDraw": "クリックしてドラッグします。離すと終了します",
|
||||
"text": "ヒント: 選択ツールを使用して任意の場所をダブルクリックしてテキストを追加することもできます",
|
||||
"embeddable": "クリックしてドラッグし、ウェブサイトを埋め込む",
|
||||
"text_selected": "",
|
||||
"text_editing": "",
|
||||
"linearElementMulti": "",
|
||||
"lockAngle": "",
|
||||
"resize": "",
|
||||
"resizeImage": "",
|
||||
"rotate": "",
|
||||
"lineEditor_info": "",
|
||||
"lineEditor_line_info": "",
|
||||
"lineEditor_pointSelected": "",
|
||||
"lineEditor_nothingSelected": "",
|
||||
"text_selected": "ダブルクリック、または {{shortcut}} を押してテキストを編集",
|
||||
"text_editing": "{{shortcut_1}} または {{shortcut_2}} を押して編集を終了",
|
||||
"linearElementMulti": "最後の点をクリック、または {{shortcut_1}} か {{shortcut_2}} を押して完了",
|
||||
"lockAngle": "{{shortcut}} を押し続けて角度を固定",
|
||||
"resize": "{{shortcut_1}} を押し続けて縦横比を維持、\n{{shortcut_2}} を押し続けて中心からリサイズ",
|
||||
"resizeImage": "{{shortcut_1}} を押し続けて自由にリサイズ、\n{{shortcut_2}} を押し続けて中心からリサイズ",
|
||||
"rotate": "{{shortcut}} を押し続けて角度を固定して回転",
|
||||
"lineEditor_info": "{{shortcut_1}} を押しながらダブルクリック、または {{shortcut_2}} を押して点を編集",
|
||||
"lineEditor_line_info": "ダブルクリック、または {{shortcut}} を押して点を編集",
|
||||
"lineEditor_pointSelected": "{{shortcut_1}} で点を削除、\n{{shortcut_2}} で複製、またはドラッグで移動",
|
||||
"lineEditor_nothingSelected": "点を選択して編集 ({{shortcut_1}} を押し続けて複数選択)、\nまたは {{shortcut_2}} を押しながらクリックで新しい点を追加",
|
||||
"publishLibrary": "自分のライブラリを公開",
|
||||
"bindTextToElement": "",
|
||||
"createFlowchart": "",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": "",
|
||||
"bindTextToElement": "{{shortcut}} でテキストを追加",
|
||||
"createFlowchart": "{{shortcut}} でフローチャートを作成",
|
||||
"deepBoxSelect": "{{shortcut}} を押し続けて詳細選択・ドラッグ防止",
|
||||
"eraserRevert": "{{shortcut}} を押し続けて削除マークを取り消し",
|
||||
"firefox_clipboard_write": "この機能は、\"dom.events.asyncClipboard.clipboardItem\" フラグを \"true\" に設定することで有効になる可能性があります。Firefox でブラウザーの設定を変更するには、\"about:config\" ページを参照してください。",
|
||||
"disableSnapping": "",
|
||||
"enterCropEditor": "",
|
||||
"leaveCropEditor": ""
|
||||
"disableSnapping": "{{shortcut}} を押し続けてスナップを無効化",
|
||||
"enterCropEditor": "画像をダブルクリック、または {{shortcut}} を押して切り抜き",
|
||||
"leaveCropEditor": "画像の外をクリック、または {{shortcut_1}} か {{shortcut_2}} を押して切り抜きを終了"
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "プレビューを表示できません",
|
||||
@@ -373,17 +388,17 @@
|
||||
},
|
||||
"errorSplash": {
|
||||
"headingMain": "エラーが発生しました。もう一度やり直してください。 <button>ページを再読み込みする。</button>",
|
||||
"clearCanvasMessage": "再読み込みがうまくいかない場合は、 <button>キャンバスを消去しています</button>",
|
||||
"clearCanvasMessage": "再読み込みがうまくいかない場合は、<button>キャンバスを消去</button>してみてください。",
|
||||
"clearCanvasCaveat": " これにより作業が失われます ",
|
||||
"trackedToSentry": "識別子のエラー {{eventId}} が我々のシステムで追跡されました。",
|
||||
"openIssueMessage": "エラーに関するシーン情報を含めないように非常に慎重に設定しました。もしあなたのシーンがプライベートでない場合は、私たちのフォローアップを検討してください。 <button>バグ報告</button> GitHub のIssueに以下の情報をコピーして貼り付けてください。",
|
||||
"trackedToSentry": "エラー (識別子: {{eventId}}) はシステムに記録されました。",
|
||||
"openIssueMessage": "エラーにシーン情報が含まれないよう配慮しています。シーンが非公開でなければ、<button>バグ報告</button>へのご協力をお願いします。以下の情報をコピーしてGitHub Issueに貼り付けてください。",
|
||||
"sceneContent": "シーンの内容:"
|
||||
},
|
||||
"shareDialog": {
|
||||
"or": "または"
|
||||
},
|
||||
"roomDialog": {
|
||||
"desc_intro": "図面にコラボレーションするよう人々を招待します。",
|
||||
"desc_intro": "他の人を描画の共同編集に招待しましょう。",
|
||||
"desc_privacy": "心配しないでください、セッションはエンドツーエンドで暗号化されており、完全にプライベートです。私たちのサーバーでさえも、あなたが描いたものを見ることができません。",
|
||||
"button_startSession": "セッションを開始する",
|
||||
"button_stopSession": "セッションを終了する",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "エラー"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "ディスクに保存",
|
||||
"disk_details": "シーンデータを後からインポートできるファイルにエクスポートします。",
|
||||
@@ -411,8 +430,8 @@
|
||||
"click": "クリック",
|
||||
"deepSelect": "深い選択",
|
||||
"deepBoxSelect": "ボックス内の深い選択、およびドラッグの抑止",
|
||||
"createFlowchart": "",
|
||||
"navigateFlowchart": "",
|
||||
"createFlowchart": "汎用要素からフローチャートを作成",
|
||||
"navigateFlowchart": "フローチャート内を移動",
|
||||
"curvedArrow": "カーブした矢印",
|
||||
"curvedLine": "曲線",
|
||||
"documentation": "ドキュメント",
|
||||
@@ -436,8 +455,8 @@
|
||||
"toggleElementLock": "選択したアイテムをロック/ロック解除",
|
||||
"movePageUpDown": "ページを上下に移動",
|
||||
"movePageLeftRight": "ページを左右に移動",
|
||||
"cropStart": "",
|
||||
"cropFinish": ""
|
||||
"cropStart": "画像を切り抜き",
|
||||
"cropFinish": "画像の切り抜きを終了"
|
||||
},
|
||||
"clearCanvasDialog": {
|
||||
"title": "キャンバスを消去"
|
||||
@@ -463,10 +482,10 @@
|
||||
"required": "必須項目",
|
||||
"website": "有効な URL を入力してください"
|
||||
},
|
||||
"noteDescription": "以下に含めるライブラリを提出してください <link>公開ライブラリのリポジトリ</link>他の人が作図に使えるようにするためです",
|
||||
"noteGuidelines": "最初にライブラリを手動で承認する必要があります。次をお読みください <link>ガイドライン</link> 送信する前に、GitHubアカウントが必要になりますが、必須ではありません。",
|
||||
"noteLicense": "提出することにより、ライブラリが次の下で公開されることに同意します: <link>MIT ライセンス </link>つまり誰でも制限なく使えるということです",
|
||||
"noteItems": "各ライブラリ項目は、フィルタリングのために独自の名前を持つ必要があります。以下のライブラリアイテムが含まれます:",
|
||||
"noteDescription": "あなたのライブラリを<link>公開ライブラリリポジトリ</link>に投稿して、他の人が作図に使えるようにしましょう。",
|
||||
"noteGuidelines": "投稿されたライブラリは担当者が審査します。投稿前に<link>ガイドライン</link>をお読みください。修正をお願いすることがあるため、GitHubアカウントがあると便利ですが、必須ではありません。",
|
||||
"noteLicense": "投稿することで、ライブラリが<link>MITライセンス</link>で公開されることに同意したものとみなします。これは誰でも自由に利用できることを意味します。",
|
||||
"noteItems": "各アイテムにはフィルタリング用に固有の名前が必要です。以下のアイテムが含まれます:",
|
||||
"atleastOneLibItem": "開始するには少なくとも1つのライブラリ項目を選択してください",
|
||||
"republishWarning": "注意: 選択された項目の中には、すでに公開/投稿済みと表示されているものがあります。既存のライブラリや投稿を更新する場合のみ、アイテムを再投稿してください。"
|
||||
},
|
||||
@@ -508,15 +527,15 @@
|
||||
},
|
||||
"stats": {
|
||||
"angle": "角度",
|
||||
"shapes": "",
|
||||
"shapes": "図形",
|
||||
"height": "高さ",
|
||||
"scene": "シーン",
|
||||
"selected": "選択済み",
|
||||
"storage": "ストレージ",
|
||||
"fullTitle": "",
|
||||
"fullTitle": "キャンバス・図形のプロパティ",
|
||||
"title": "プロパティ",
|
||||
"generalStats": "",
|
||||
"elementProperties": "",
|
||||
"generalStats": "全般",
|
||||
"elementProperties": "図形のプロパティ",
|
||||
"total": "合計",
|
||||
"version": "バージョン",
|
||||
"versionCopy": "クリックしてコピー",
|
||||
@@ -528,7 +547,7 @@
|
||||
"copyStyles": "スタイルをコピーしました。",
|
||||
"copyToClipboard": "クリップボードにコピー",
|
||||
"copyToClipboardAsPng": "{{exportSelection}} を PNG 形式でクリップボードにコピーしました\n({{exportColorScheme}})",
|
||||
"copyToClipboardAsSvg": "",
|
||||
"copyToClipboardAsSvg": "{{exportSelection}} を SVG 形式でクリップボードにコピーしました\n({{exportColorScheme}})",
|
||||
"fileSaved": "ファイルを保存しました",
|
||||
"fileSavedToFilename": "{filename} に保存しました",
|
||||
"canvas": "キャンバス",
|
||||
@@ -536,7 +555,7 @@
|
||||
"pasteAsSingleElement": "{{shortcut}} を使用して単一の要素として貼り付けるか、\n既存のテキストエディタに貼り付け",
|
||||
"unableToEmbed": "この URL の埋め込みは現在許可されていません。URL のホワイトリストへの追加をリクエストするには、GitHub で Issue を上げてください。",
|
||||
"unrecognizedLinkFormat": "埋め込もうとしたリンクは期待するフォーマットと一致しません。埋め込み元のサイトで提供される「embed」の文字列を貼り付けてください。",
|
||||
"elementLinkCopied": ""
|
||||
"elementLinkCopied": "リンクをクリップボードにコピーしました"
|
||||
},
|
||||
"colors": {
|
||||
"transparent": "透明",
|
||||
@@ -557,9 +576,9 @@
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_line2": "",
|
||||
"center_heading_line3": "",
|
||||
"center_heading": "描画データはブラウザーのストレージに保存されます。",
|
||||
"center_heading_line2": "ブラウザーのストレージは予期せず消去されることがあります。",
|
||||
"center_heading_line3": "作業を失わないよう、定期的にファイルへ保存してください。",
|
||||
"center_heading_plus": "代わりにExcalidraw+を開きますか?",
|
||||
"menuHint": "エクスポート、設定、言語..."
|
||||
},
|
||||
@@ -571,7 +590,7 @@
|
||||
}
|
||||
},
|
||||
"colorPicker": {
|
||||
"color": "",
|
||||
"color": "色",
|
||||
"mostUsedCustomColors": "最も使用されているカスタム色",
|
||||
"colors": "色",
|
||||
"shades": "影",
|
||||
@@ -600,76 +619,77 @@
|
||||
"loadFromFile": {
|
||||
"title": "ファイルからロード",
|
||||
"button": "ファイルからロード",
|
||||
"description": "ファイルからのロードは、<bold>現在の描画内容を置き換えます</bold>。<br></br>その前に、以下の選択肢のいずれかにより描画内容を保存できます。"
|
||||
"description": "ファイルからの読み込みは、<bold>現在の描画内容を置き換えます</bold>。<br></br>その前に、以下の選択肢のいずれかにより描画内容を保存できます。"
|
||||
},
|
||||
"shareableLink": {
|
||||
"title": "リンクからロード",
|
||||
"title": "リンクから読み込み",
|
||||
"button": "描画内容を置き換える",
|
||||
"description": "外部図面のロードは、<bold>現在の描画内容を置き換えます</bold>。<br></br>その前に、以下の選択肢のいずれかにより描画内容を保存できます。"
|
||||
"description": "外部の描画データの読み込みは、<bold>現在の描画内容を置き換えます</bold>。<br></br>その前に、以下の選択肢のいずれかにより描画内容を保存できます。"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mermaid": {
|
||||
"title": "Mermaid を Excalidraw に変換",
|
||||
"button": "挿入",
|
||||
"description": "現在、<flowchartLink>Flowchart</flowchartLink>、<sequenceLink>Sequence</sequenceLink>、<classLink>Class</classLink> のダイアグラムのみに対応しています。その他の種類は、Excalidraw では画像として描画されます。",
|
||||
"description": "",
|
||||
"syntax": "Mermaid 構文",
|
||||
"preview": "プレビュー",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"label": "Mermaid",
|
||||
"inputPlaceholder": "Mermaid のダイアグラム定義をここに入力...",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
"error": "エラー!"
|
||||
},
|
||||
"chat": {
|
||||
"inputPlaceholder": "",
|
||||
"inputPlaceholderWithMessages": "",
|
||||
"generating": "",
|
||||
"rateLimitRemaining": "",
|
||||
"inputPlaceholder": "ダイアグラムのアイデアを入力... ({{shortcut}} で改行)",
|
||||
"inputPlaceholderWithMessages": "ダイアグラムを修正...",
|
||||
"generating": "生成中...",
|
||||
"rateLimitRemaining": "本日の残りリクエスト回数: {{count}}",
|
||||
"role": {
|
||||
"user": "",
|
||||
"assistant": "",
|
||||
"system": ""
|
||||
"user": "あなた",
|
||||
"assistant": "AIアシスタント",
|
||||
"system": "システム"
|
||||
},
|
||||
"aiBeta": "",
|
||||
"label": "",
|
||||
"menu": "",
|
||||
"newChat": "",
|
||||
"deleteChat": "",
|
||||
"deleteMessage": "",
|
||||
"viewAsMermaid": "",
|
||||
"aiBeta": "AI ベータ版",
|
||||
"label": "チャット",
|
||||
"menu": "メニュー",
|
||||
"newChat": "新しいチャット",
|
||||
"deleteChat": "チャットを削除",
|
||||
"deleteMessage": "メッセージを削除",
|
||||
"viewAsMermaid": "Mermaidで表示",
|
||||
"placeholder": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "ダイアグラムをデザインしよう",
|
||||
"description": "作りたいダイアグラムを説明してください。AIが生成します。",
|
||||
"hint": ""
|
||||
},
|
||||
"preview": "",
|
||||
"insert": "",
|
||||
"retry": "",
|
||||
"preview": "プレビュー",
|
||||
"insert": "挿入",
|
||||
"retry": "再試行",
|
||||
"errors": {
|
||||
"promptTooShort": "",
|
||||
"promptTooLong": "",
|
||||
"generationFailed": "",
|
||||
"invalidDiagram": "",
|
||||
"fixInMermaid": "",
|
||||
"aiRepair": "",
|
||||
"requestAborted": "",
|
||||
"requestFailed": "",
|
||||
"mermaidParseError": ""
|
||||
"promptTooShort": "プロンプトが短すぎます ({{min}} 文字以上)",
|
||||
"promptTooLong": "プロンプトが長すぎます ({{max}} 文字以下)",
|
||||
"generationFailed": "生成に失敗しました",
|
||||
"invalidDiagram": "無効なダイアグラムが生成されました。手動で編集、自動修正で再試行、または別のプロンプトをお試しください。",
|
||||
"fixInMermaid": "Mermaidで手動編集 →",
|
||||
"aiRepair": "再生成 (自動修正) →",
|
||||
"requestAborted": "リクエストが中断されました",
|
||||
"requestFailed": "リクエストに失敗しました",
|
||||
"mermaidParseError": "Mermaid構文エラー"
|
||||
},
|
||||
"rateLimit": {
|
||||
"messageLimit": "",
|
||||
"generalRateLimit": "",
|
||||
"messageLimitInputPlaceholder": ""
|
||||
"messageLimit": "無料プランのAI利用上限に達しました。Excalidraw+をお試しいただくか、また明日ご利用ください。",
|
||||
"generalRateLimit": "操作が速すぎたようです。もう少し待ってから再度お試しください。",
|
||||
"messageLimitInputPlaceholder": "メッセージの送信上限に達しました"
|
||||
},
|
||||
"upsellBtnLabel": ""
|
||||
"upsellBtnLabel": "Plusにアップグレード"
|
||||
},
|
||||
"quickSearch": {
|
||||
"placeholder": ""
|
||||
"placeholder": "クイック検索"
|
||||
},
|
||||
"fontList": {
|
||||
"badge": {
|
||||
"old": ""
|
||||
"old": "旧"
|
||||
},
|
||||
"sceneFonts": "このシーン内",
|
||||
"availableFonts": "利用可能フォント",
|
||||
@@ -680,36 +700,36 @@
|
||||
"hint": {
|
||||
"text": "ユーザーをクリックしてフォロー",
|
||||
"followStatus": "現在このユーザーをフォローしています",
|
||||
"inCall": "",
|
||||
"micMuted": "",
|
||||
"isSpeaking": ""
|
||||
"inCall": "ユーザーは音声通話中です",
|
||||
"micMuted": "ユーザーのマイクはミュート中です",
|
||||
"isSpeaking": "ユーザーは発話中です"
|
||||
}
|
||||
},
|
||||
"commandPalette": {
|
||||
"title": "",
|
||||
"title": "コマンドパレット",
|
||||
"shortcuts": {
|
||||
"select": "選択",
|
||||
"confirm": "確認",
|
||||
"close": "閉じる"
|
||||
},
|
||||
"recents": "",
|
||||
"recents": "最近使ったもの",
|
||||
"search": {
|
||||
"placeholder": "",
|
||||
"noMatch": ""
|
||||
"placeholder": "メニュー、コマンドを検索して便利な機能を見つけよう",
|
||||
"noMatch": "一致するコマンドはありません..."
|
||||
},
|
||||
"itemNotAvailable": "",
|
||||
"itemNotAvailable": "コマンドを利用できません...",
|
||||
"shortcutHint": "コマンドパレットには{{shortcut}}を使用"
|
||||
},
|
||||
"keys": {
|
||||
"ctrl": "",
|
||||
"option": "",
|
||||
"cmd": "",
|
||||
"alt": "",
|
||||
"escape": "",
|
||||
"enter": "",
|
||||
"shift": "",
|
||||
"spacebar": "",
|
||||
"delete": "",
|
||||
"mmb": ""
|
||||
"ctrl": "Ctrl",
|
||||
"option": "Option",
|
||||
"cmd": "Cmd",
|
||||
"alt": "Alt",
|
||||
"escape": "Esc",
|
||||
"enter": "Enter",
|
||||
"shift": "Shift",
|
||||
"spacebar": "スペース",
|
||||
"delete": "Delete",
|
||||
"mmb": "スクロールホイール"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Qoyıw",
|
||||
"pasteAsPlaintext": "Ápiwayı tekst retinde qoyıw",
|
||||
"pasteCharts": "Diagrammalardı qoyıw",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Barlıǵın tańlaw",
|
||||
"multiSelect": "",
|
||||
"moveCanvas": "",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Qátelik"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Diskke saqlaw",
|
||||
"disk_details": "",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Senṭeḍ",
|
||||
"pasteAsPlaintext": "",
|
||||
"pasteCharts": "Senṭeḍ udlifen",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Fren akk",
|
||||
"multiSelect": "Rnu aferdis ɣer tefrayt",
|
||||
"moveCanvas": "Smutti taɣzut n usuneɣ",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Tuccḍa"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Sekles deg uḍebsi",
|
||||
"disk_details": "Sekles isefka n usayes deg ufaylu ansi ara tizmireḍ ad d-tketreḍ areḍqal.",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Қою",
|
||||
"pasteAsPlaintext": "",
|
||||
"pasteCharts": "Диаграммаларды қою",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Бәрін таңдау",
|
||||
"multiSelect": "",
|
||||
"moveCanvas": "",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Қате"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "",
|
||||
"disk_details": "Сахна деректерін кейін қайта импорттауға болатын файлға экспорттаңыз.",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "បិទភ្ជាប់",
|
||||
"pasteAsPlaintext": "បិទភ្ជាប់ជាអត្ថបទធម្មតា",
|
||||
"pasteCharts": "បិទភ្ជាប់តារាង",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "ជ្រើសរើសទាំងអស់",
|
||||
"multiSelect": "បន្ថែមធាតុទៅលើការជ្រើសរើស",
|
||||
"moveCanvas": "ផ្លាស់ទីបាវ",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "មានកំហុស"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "រក្សាទុកទៅថាស",
|
||||
"disk_details": "នាំចេញទិន្នន័យរបស់ស៊ីនជាឯកសារដែលអ្នកអាចនាំចូលនៅពេលក្រោយ។",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "붙여넣기",
|
||||
"pasteAsPlaintext": "일반 텍스트로 붙여넣기",
|
||||
"pasteCharts": "차트 붙여넣기",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "전체 선택",
|
||||
"multiSelect": "선택 영역에 추가하기",
|
||||
"moveCanvas": "캔버스 이동",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "화살표 모양",
|
||||
"arrowtype_sharp": "뾰족한 화살표",
|
||||
"arrowtype_round": "곡선 화살표",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "오류"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "디스크에 저장",
|
||||
"disk_details": "나중에 다시 불러올 수 있도록 화면 데이터를 내보냅니다.",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "دانانەوە",
|
||||
"pasteAsPlaintext": "دایبنێ وەک دەقی سادە",
|
||||
"pasteCharts": "دانانەوەی خشتەکان",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "دیاریکردنی هەموو",
|
||||
"multiSelect": "زیادکردنی بۆ دیاریکراوەکان",
|
||||
"moveCanvas": "تابلۆ بجوڵێنە",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "ههڵه ڕوویدا"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "پاشەکەوت بکە لە دیسک",
|
||||
"disk_details": "هەناردەکردنی داتای دیمەنەکە بۆ فایلێک کە دواتر دەتوانیت لێی هاوردە بکەیت.",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Įklijuoti",
|
||||
"pasteAsPlaintext": "Įklijuoti kaip paprastą tekstą",
|
||||
"pasteCharts": "Įklijuoti diagramas",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Pažymėti viską",
|
||||
"multiSelect": "Pridėkite elementą prie pasirinktų",
|
||||
"moveCanvas": "Judinti drobę",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Klaida"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Įrašyti į diską",
|
||||
"disk_details": "",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Ielīmēt",
|
||||
"pasteAsPlaintext": "Ielīmēt kā vienkāršu tekstu",
|
||||
"pasteCharts": "Ielīmēt grafikus",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Atlasīt visu",
|
||||
"multiSelect": "Pievienot elementu atlasei",
|
||||
"moveCanvas": "Pārvietot tāfeli",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Kļūda"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Saglabāt diskā",
|
||||
"disk_details": "Eksportēt ainas datus datnē, ko vēlāk varēsiet importēt.",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "चिटकवा",
|
||||
"pasteAsPlaintext": "साधा मजकूर च्या रुपात पेस्ट करा",
|
||||
"pasteCharts": "चार्ट चिकटवा",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "समस्त निवडा",
|
||||
"multiSelect": "निवडित तत्व जोडा",
|
||||
"moveCanvas": "पटल हलवा",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "चिमणि पाय (अनेक)",
|
||||
"arrowhead_crowfoot_one": "चिमणि पाय (एक)",
|
||||
"arrowhead_crowfoot_one_or_many": "चिमणि पाय (एक अथवा अनेक)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "आणिक विकल्प",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "बाणाचे प्रकार",
|
||||
"arrowtype_sharp": "तीक्ष्ण तीर",
|
||||
"arrowtype_round": "वक्राकार तीर",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "वस्तू ह्याचा दुवा",
|
||||
"wrapSelectionInFrame": "चौकटित निवडलेले गुंडाळा",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "वस्तू ह्याचा दुवा",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "त्रुटि"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "डिस्क मधे जतन करा",
|
||||
"disk_details": "सीन डेटा बाहेर एक फ़ाइल मधे जतन करा, त्या फ़ाइल मधुम तो डेटा नंतर परत आणु शकता.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "मर्मेड पासून एक्सकाली मधे",
|
||||
"button": "शिरवा",
|
||||
"description": "सध्या फक्त <flowchartLink> प्रवाह चित्र (फ़्लो चार्ट) </flowchartLink> आणि <sequenceLink> क्रम चित्र (सिकवेंस ड़ायग्राम) </sequenceLink> करता येतात. बाक़ीचे चित्र प्रकार एक्सकाली चित्र पद्धति नी चित्रित होतील.",
|
||||
"description": "",
|
||||
"syntax": "मर्मेड संरचना नियम",
|
||||
"preview": "पूर्वावलोकन",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "ထား",
|
||||
"pasteAsPlaintext": "",
|
||||
"pasteCharts": "",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "အကုန်ရွေး",
|
||||
"multiSelect": "ရွေးထားသည့်ထဲပုံထည့်",
|
||||
"moveCanvas": "ကားချပ်ရွှေ့",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "ချို့ယွင်းချက်"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "",
|
||||
"disk_details": "",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Lim inn",
|
||||
"pasteAsPlaintext": "Lim inn som klartekst",
|
||||
"pasteCharts": "Lim inn diagrammer",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Velg alt",
|
||||
"multiSelect": "Legg til element i utvalg",
|
||||
"moveCanvas": "Flytt lerretet",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Kråkefot (mange)",
|
||||
"arrowhead_crowfoot_one": "Kråkefot (én)",
|
||||
"arrowhead_crowfoot_one_or_many": "Kråkefot (én eller mange)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Flere alternativer",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Type pil",
|
||||
"arrowtype_sharp": "Skarp pil",
|
||||
"arrowtype_round": "Buet pil",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Lenke til objekt",
|
||||
"wrapSelectionInFrame": "Brekk om utvalg i ramme",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Lenke til objekt",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Feil"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Lagre til disk",
|
||||
"disk_details": "Eksporter scene-dataene til en fil som du kan importere fra senere.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid til Excalidraw",
|
||||
"button": "Sett inn",
|
||||
"description": "Foreløpig er bare <flowchartLink>Flowchart</flowchartLink>-,<sequenceLink> Sequence</sequenceLink>- og <classLink>klasse </classLink>-diagrammer støttet. De andre typene vil bli gjengitt som bilde i Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Mermaid-syntaks",
|
||||
"preview": "Forhåndsvisning",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Plakken",
|
||||
"pasteAsPlaintext": "Plakken als platte tekst",
|
||||
"pasteCharts": "Grafieken plakken",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Alles selecteren",
|
||||
"multiSelect": "Voeg element toe aan selectie",
|
||||
"moveCanvas": "Canvas verplaatsen",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Kraaienpoot (veel)",
|
||||
"arrowhead_crowfoot_one": "Kraaienpoot (enkel)",
|
||||
"arrowhead_crowfoot_one_or_many": "Kraaienpoot (enkel of veel)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Meer opties",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Pijl type",
|
||||
"arrowtype_sharp": "Scherpe pijl",
|
||||
"arrowtype_round": "Gebogen pijl",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Link naar object",
|
||||
"wrapSelectionInFrame": "Selectie omslaan in frame",
|
||||
"tab": "Tab",
|
||||
"shapeSwitch": "Verander vorm"
|
||||
"shapeSwitch": "Verander vorm",
|
||||
"preferences": "Voorkeuren",
|
||||
"preferences_toolLock": "Tool vergrendelen",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Link naar object",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Fout"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Opslaan op schijf",
|
||||
"disk_details": "De scènegegevens exporteren naar een bestand waaruit u later kunt importeren.",
|
||||
@@ -557,9 +576,9 @@
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_line2": "",
|
||||
"center_heading_line3": "",
|
||||
"center_heading": "Je tekeningen worden bewaard in de opslag van je browser.",
|
||||
"center_heading_line2": "Browser opslag kan onverwacht gewist worden.",
|
||||
"center_heading_line3": "Sla je werk regelmatig op in een bestand om te voorkomen dat je het kwijtraakt.",
|
||||
"center_heading_plus": "Wil je in plaats daarvan naar Excalidraw+ gaan?",
|
||||
"menuHint": "Exporteren, voorkeuren en meer, ..."
|
||||
},
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid naar Excalidraw",
|
||||
"button": "Invoegen",
|
||||
"description": "Momenteel worden alleen <flowchartLink>Flowchart</flowchartLink>-, <sequenceLink>Sequence</sequenceLink>- en <classLink>Class</classLink>-diagrammen ondersteund. De andere types worden als afbeelding weergegeven in Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Mermaid Syntaxis",
|
||||
"preview": "Voorbeeld",
|
||||
"label": "Mermaid",
|
||||
"inputPlaceholder": "Noteer Mermaid diagram omschrijving hier..."
|
||||
"inputPlaceholder": "Noteer Mermaid diagram omschrijving hier...",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": "Fout!"
|
||||
@@ -624,7 +644,7 @@
|
||||
"chat": {
|
||||
"inputPlaceholder": "Begin je diagram idee hier in te typen... ({{shortcut}} voor een nieuwe lijn)",
|
||||
"inputPlaceholderWithMessages": "Ga door met het verfijnen van je diagram...",
|
||||
"generating": "",
|
||||
"generating": "Genereren...",
|
||||
"rateLimitRemaining": "{{count}} verzoeken over vandaag",
|
||||
"role": {
|
||||
"user": "Jij",
|
||||
@@ -641,7 +661,7 @@
|
||||
"placeholder": {
|
||||
"title": "Laten we jouw diagram ontwerpen",
|
||||
"description": "Beschrijf het diagram dat je wilt aanmaken, en we genereren het voor je.",
|
||||
"hint": "Op dit moment kennen we Flowchart, Sequence, en Class diagrammen."
|
||||
"hint": ""
|
||||
},
|
||||
"preview": "Voorbeeld",
|
||||
"insert": "Invoegen",
|
||||
@@ -655,7 +675,7 @@
|
||||
"aiRepair": "Opnieuw genereren (automatisch repareren) →",
|
||||
"requestAborted": "Verzoek gestopt",
|
||||
"requestFailed": "Verzoek mislukt",
|
||||
"mermaidParseError": ""
|
||||
"mermaidParseError": "Mermaid syntax fout"
|
||||
},
|
||||
"rateLimit": {
|
||||
"messageLimit": "Je hebt je AI limiet bereikt op het gratis abonnement. Probeer Excalidraw+ uit voor meer of kom morgen terug.",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Lim inn",
|
||||
"pasteAsPlaintext": "",
|
||||
"pasteCharts": "Lim inn diagram",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Vel alt",
|
||||
"multiSelect": "Legg til element i utval",
|
||||
"moveCanvas": "Flytt lerretet",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Feil"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Lagre til disk",
|
||||
"disk_details": "Eksporter scenedataa til ei fil du kan importere seinare.",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Pegar",
|
||||
"pasteAsPlaintext": "Pegar en tèxt brut",
|
||||
"pasteCharts": "Pegar los grafics",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Tot seleccionar",
|
||||
"multiSelect": "Apondre un element a la seleccion",
|
||||
"moveCanvas": "Desplaçar lo canabàs",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Ligam cap a l’objècte",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "Onglet",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Error"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Salvar al disc",
|
||||
"disk_details": "Exportar las donadas de la scèna cap a un fichièr que podètz importar mai tard.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "De Mermaid cap a Excalidraw",
|
||||
"button": "Inserir",
|
||||
"description": "Actualament, sonque los diagramas <flowchartLink>logics</flowchartLink>,<sequenceLink> de sequéncia</sequenceLink> e <classLink>de classa </classLink>son preses en carga. Los autres tipes seràn afichats coma imatge dins Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Sintaxi Mermaid",
|
||||
"preview": "Apercebut",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "ਪੇਸਟ ਕਰੋ",
|
||||
"pasteAsPlaintext": "ਸਾਦੇ ਪਾਠ ਵਜੋਂ ਪੇਸਟ ਕਰੋ",
|
||||
"pasteCharts": "ਚਾਰਟ ਪੇਸਟ ਕਰੋ",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "ਸਾਰੇ ਚੁਣੋ",
|
||||
"multiSelect": "ਐਲੀਮੈਂਟ ਨੂੰ ਚੋਣ ਵਿੱਚ ਜੋੜੋ",
|
||||
"moveCanvas": "ਕੈਨਵਸ ਹਿਲਾਓ",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "ਗਲਤੀ"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "ਡਿਸਕ ਵਿੱਚ ਸਾਂਭੋ",
|
||||
"disk_details": "ਦ੍ਰਿਸ਼ ਦਾ ਡਾਟਾ ਫਾਈਲ ਵਿੱਚ ਨਿਰਯਾਤ ਕਰੋ ਜਿੱਥੋਂ ਤੁਸੀਂ ਇਸਨੂੰ ਬਾਅਦ ਵਿੱਚ ਆਯਾਤ ਕਰ ਸਕਦੇ ਹੋ।",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -1,59 +1,59 @@
|
||||
{
|
||||
"ar-SA": 84,
|
||||
"az-AZ": 25,
|
||||
"bg-BG": 65,
|
||||
"bn-BD": 39,
|
||||
"bn-IN": 39,
|
||||
"ca-ES": 84,
|
||||
"cs-CZ": 76,
|
||||
"ar-SA": 82,
|
||||
"az-AZ": 24,
|
||||
"bg-BG": 63,
|
||||
"bn-BD": 38,
|
||||
"bn-IN": 38,
|
||||
"ca-ES": 82,
|
||||
"cs-CZ": 74,
|
||||
"da-DK": 28,
|
||||
"de-CH": 85,
|
||||
"de-DE": 85,
|
||||
"el-GR": 80,
|
||||
"de-CH": 83,
|
||||
"de-DE": 83,
|
||||
"el-GR": 77,
|
||||
"en": 100,
|
||||
"es-ES": 85,
|
||||
"eu-ES": 72,
|
||||
"fa-IR": 84,
|
||||
"fi-FI": 62,
|
||||
"fr-FR": 92,
|
||||
"gl-ES": 63,
|
||||
"he-IL": 84,
|
||||
"hi-IN": 86,
|
||||
"hu-HU": 67,
|
||||
"id-ID": 84,
|
||||
"it-IT": 99,
|
||||
"ja-JP": 81,
|
||||
"kaa": 22,
|
||||
"kab-KAB": 56,
|
||||
"es-ES": 82,
|
||||
"eu-ES": 70,
|
||||
"fa-IR": 83,
|
||||
"fi-FI": 60,
|
||||
"fr-FR": 97,
|
||||
"gl-ES": 61,
|
||||
"he-IL": 82,
|
||||
"hi-IN": 83,
|
||||
"hu-HU": 65,
|
||||
"id-ID": 81,
|
||||
"it-IT": 98,
|
||||
"ja-JP": 96,
|
||||
"kaa": 21,
|
||||
"kab-KAB": 54,
|
||||
"kk-KZ": 13,
|
||||
"km-KH": 56,
|
||||
"ko-KR": 75,
|
||||
"ku-TR": 59,
|
||||
"lt-LT": 35,
|
||||
"lv-LV": 53,
|
||||
"mr-IN": 84,
|
||||
"my-MM": 25,
|
||||
"nb-NO": 84,
|
||||
"nl-NL": 99,
|
||||
"nn-NO": 45,
|
||||
"oc-FR": 79,
|
||||
"pa-IN": 54,
|
||||
"pl-PL": 93,
|
||||
"pt-BR": 84,
|
||||
"pt-PT": 84,
|
||||
"ro-RO": 100,
|
||||
"ru-RU": 99,
|
||||
"si-LK": 70,
|
||||
"sk-SK": 93,
|
||||
"sl-SI": 84,
|
||||
"sv-SE": 85,
|
||||
"ta-IN": 81,
|
||||
"th-TH": 59,
|
||||
"tr-TR": 88,
|
||||
"uk-UA": 84,
|
||||
"km-KH": 54,
|
||||
"ko-KR": 73,
|
||||
"ku-TR": 58,
|
||||
"lt-LT": 33,
|
||||
"lv-LV": 51,
|
||||
"mr-IN": 82,
|
||||
"my-MM": 24,
|
||||
"nb-NO": 82,
|
||||
"nl-NL": 96,
|
||||
"nn-NO": 43,
|
||||
"oc-FR": 76,
|
||||
"pa-IN": 52,
|
||||
"pl-PL": 90,
|
||||
"pt-BR": 86,
|
||||
"pt-PT": 82,
|
||||
"ro-RO": 98,
|
||||
"ru-RU": 98,
|
||||
"si-LK": 67,
|
||||
"sk-SK": 90,
|
||||
"sl-SI": 82,
|
||||
"sv-SE": 82,
|
||||
"ta-IN": 78,
|
||||
"th-TH": 65,
|
||||
"tr-TR": 92,
|
||||
"uk-UA": 86,
|
||||
"uz-UZ": 0,
|
||||
"vi-VN": 70,
|
||||
"zh-CN": 93,
|
||||
"vi-VN": 68,
|
||||
"zh-CN": 91,
|
||||
"zh-HK": 16,
|
||||
"zh-TW": 84
|
||||
"zh-TW": 98
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Wklej",
|
||||
"pasteAsPlaintext": "Wklej jako zwykły tekst",
|
||||
"pasteCharts": "Wklej wykresy",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Zaznacz wszystko",
|
||||
"multiSelect": "Dodaj element do zaznaczenia",
|
||||
"moveCanvas": "Przesuń obszar roboczy",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Notacja Martina (wiele)",
|
||||
"arrowhead_crowfoot_one": "Notacja Martina (jeden)",
|
||||
"arrowhead_crowfoot_one_or_many": "Notacja Martina (jeden lub wiele)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Więcej opcji",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Typ strzałki",
|
||||
"arrowtype_sharp": "Ostra strzałka",
|
||||
"arrowtype_round": "Zaokrąglona strzałka",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Link do obiektu",
|
||||
"wrapSelectionInFrame": "Owiń wybór w ramkę",
|
||||
"tab": "Tab",
|
||||
"shapeSwitch": "Wybór kształtu"
|
||||
"shapeSwitch": "Wybór kształtu",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Link do obiektu",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Wystąpił błąd"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Zapisz na dysku",
|
||||
"disk_details": "Eksportuj dane sceny do pliku, z którego możesz importować później.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Konwertuj diagram Mermaid do Excalidraw",
|
||||
"button": "Wstaw",
|
||||
"description": "Obecnie wspierane są jedynie <flowchartLink>proste grafy</flowchartLink>, <sequenceLink>sekwencje</sequenceLink> i <classLink>diagramy klas</classLink>. Pozostałe typy będą wyświetlane jako obrazy w Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Składnia diagramów Mermaid",
|
||||
"preview": "Podgląd",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Colar",
|
||||
"pasteAsPlaintext": "Colar como texto sem formatação",
|
||||
"pasteCharts": "Colar gráficos",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Selecionar tudo",
|
||||
"multiSelect": "Adicionar elemento à seleção",
|
||||
"moveCanvas": "Mover tela",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Pé de pássaro (muitos)",
|
||||
"arrowhead_crowfoot_one": "Pé de pássaro (um)",
|
||||
"arrowhead_crowfoot_one_or_many": "Pé de pássaro (um ou muitos)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Mais opções",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Tipo de seta",
|
||||
"arrowtype_sharp": "Seta afiada",
|
||||
"arrowtype_round": "Seta curva",
|
||||
@@ -135,15 +146,15 @@
|
||||
"labelEmbed": "Vincular e incorporar",
|
||||
"empty": "Nenhum link foi definido",
|
||||
"hint": "Digite ou cole o seu link aqui",
|
||||
"goToElement": ""
|
||||
"goToElement": "Ir para o elemento alvo"
|
||||
},
|
||||
"lineEditor": {
|
||||
"edit": "Editar linha",
|
||||
"editArrow": "Editar seta"
|
||||
},
|
||||
"polygon": {
|
||||
"breakPolygon": "",
|
||||
"convertToPolygon": ""
|
||||
"breakPolygon": "Parar polígono",
|
||||
"convertToPolygon": "Converter para polígono"
|
||||
},
|
||||
"elementLock": {
|
||||
"lock": "Bloquear",
|
||||
@@ -170,11 +181,15 @@
|
||||
"copyElementLink": "Copiar link para o objeto",
|
||||
"linkToElement": "Links para o objeto",
|
||||
"wrapSelectionInFrame": "Ajustar seleção no frame",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"tab": "Aba",
|
||||
"shapeSwitch": "Trocar forma",
|
||||
"preferences": "Preferências",
|
||||
"preferences_toolLock": "Bloqueio de ferramenta",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
"title": "Links para o objeto",
|
||||
"desc": "Clique em uma forma na tela ou cole um link.",
|
||||
"notFound": "Objeto vinculado não foi encontrado na tela."
|
||||
},
|
||||
@@ -184,9 +199,9 @@
|
||||
"hint_emptyPrivateLibrary": "Selecione um item na tela para adicioná-lo aqui.",
|
||||
"search": {
|
||||
"inputPlaceholder": "",
|
||||
"heading": "",
|
||||
"noResults": "",
|
||||
"clearSearch": ""
|
||||
"heading": "Correspondências da biblioteca",
|
||||
"noResults": "Nenhum resultado encontrado...",
|
||||
"clearSearch": "Limpar pesquisa"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
@@ -195,8 +210,8 @@
|
||||
"singleResult": "resultado",
|
||||
"multipleResults": "resultados",
|
||||
"placeholder": "Procurar texto na tela...",
|
||||
"frames": "",
|
||||
"texts": ""
|
||||
"frames": "Quadros",
|
||||
"texts": "Textos"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "Limpar a tela",
|
||||
@@ -230,7 +245,7 @@
|
||||
"objectsSnapMode": "Encaixar em objetos",
|
||||
"exitZenMode": "Sair do modo zen",
|
||||
"cancel": "Cancelar",
|
||||
"saveLibNames": "",
|
||||
"saveLibNames": "Salvar nome(s) e sair",
|
||||
"clear": "Limpar",
|
||||
"remove": "Remover",
|
||||
"embed": "Alternar incorporação",
|
||||
@@ -261,7 +276,7 @@
|
||||
"removeItemsFromsLibrary": "Excluir {{count}} item(ns) da biblioteca?",
|
||||
"invalidEncryptionKey": "A chave de encriptação deve ter 22 caracteres. A colaboração ao vivo está desabilitada.",
|
||||
"collabOfflineWarning": "Sem conexão com a internet disponível.\nSuas alterações não serão salvas!",
|
||||
"localStorageQuotaExceeded": ""
|
||||
"localStorageQuotaExceeded": "A cota de armazenamento do navegador excedida. As alterações não serão salvas."
|
||||
},
|
||||
"errors": {
|
||||
"unsupportedFileType": "Tipo de arquivo não suportado.",
|
||||
@@ -313,7 +328,7 @@
|
||||
"hand": "Mão (ferramenta de rolagem)",
|
||||
"extraTools": "Mais ferramentas",
|
||||
"mermaidToExcalidraw": "Mermaid para Excalidraw",
|
||||
"convertElementType": ""
|
||||
"convertElementType": "Alternar tipo de forma"
|
||||
},
|
||||
"element": {
|
||||
"rectangle": "Retângulo",
|
||||
@@ -337,7 +352,7 @@
|
||||
"shapes": "Formas"
|
||||
},
|
||||
"hints": {
|
||||
"dismissSearch": "",
|
||||
"dismissSearch": "{{shortcut}} para descartar a pesquisa",
|
||||
"canvasPanning": "",
|
||||
"linearElement": "Clique para iniciar vários pontos, arraste para uma única linha",
|
||||
"arrowTool": "",
|
||||
@@ -357,8 +372,8 @@
|
||||
"lineEditor_pointSelected": "",
|
||||
"lineEditor_nothingSelected": "",
|
||||
"publishLibrary": "Publicar sua própria biblioteca",
|
||||
"bindTextToElement": "",
|
||||
"createFlowchart": "",
|
||||
"bindTextToElement": "{{shortcut}} para adicionar texto",
|
||||
"createFlowchart": "{{shortcut}} para criar um fluxo",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": "",
|
||||
"firefox_clipboard_write": "Esse recurso pode ser ativado configurando a opção \"dom.events.asyncClipboard.clipboardItem\" como \"true\". Para alterar os sinalizadores do navegador no Firefox, visite a página \"about:config\".",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Erro"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Salvar no computador",
|
||||
"disk_details": "Exportar os dados da cena para um arquivo que você poderá importar mais tarde.",
|
||||
@@ -536,7 +555,7 @@
|
||||
"pasteAsSingleElement": "Use {{shortcut}} para colar como um único elemento,\nou cole em um editor de texto já existente",
|
||||
"unableToEmbed": "No momento não é permitido incorporar esta URL. Crie uma issue no GitHub para solicitar a lista branca da URL",
|
||||
"unrecognizedLinkFormat": "O link incorporado não corresponde ao formato esperado. Por favor, tente colar a string 'incorporada' que foi fornecida pelo site de origem",
|
||||
"elementLinkCopied": ""
|
||||
"elementLinkCopied": "Link copiado para a área de transferência"
|
||||
},
|
||||
"colors": {
|
||||
"transparent": "Transparente",
|
||||
@@ -557,8 +576,8 @@
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_line2": "",
|
||||
"center_heading": "Seus desenhos estão salvos no armazenamento do seu navegador.",
|
||||
"center_heading_line2": "O armazenamento do navegador pode ser apagado inesperadamente.",
|
||||
"center_heading_line3": "",
|
||||
"center_heading_plus": "Você queria ir para o Excalidraw+ em vez disso?",
|
||||
"menuHint": "Exportar, preferências, idiomas..."
|
||||
@@ -571,7 +590,7 @@
|
||||
}
|
||||
},
|
||||
"colorPicker": {
|
||||
"color": "",
|
||||
"color": "Cor",
|
||||
"mostUsedCustomColors": "Cores personalizadas mais usadas",
|
||||
"colors": "Cores",
|
||||
"shades": "Tons",
|
||||
@@ -612,18 +631,19 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid para Excalidraw",
|
||||
"button": "Inserir",
|
||||
"description": "Atualmente apenas os diagramas<flowchartLink>Flowchart</flowchartLink><sequenceLink>Sequência,</sequenceLink> e <classLink>Class</classLink>são suportados. Os outros tipos serão renderizados como uma imagem no Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Sintaxe em Mermaid",
|
||||
"preview": "Visualizar",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"label": "Mermaid",
|
||||
"inputPlaceholder": "Escreva definição de diagrama Mermaid aqui...",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
"error": "Erro!"
|
||||
},
|
||||
"chat": {
|
||||
"inputPlaceholder": "",
|
||||
"inputPlaceholderWithMessages": "",
|
||||
"inputPlaceholderWithMessages": "Continuar refinando seu diagrama...",
|
||||
"generating": "",
|
||||
"rateLimitRemaining": "",
|
||||
"role": {
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Colar",
|
||||
"pasteAsPlaintext": "Colar como texto simples",
|
||||
"pasteCharts": "Colar gráficos",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Selecionar tudo",
|
||||
"multiSelect": "Adicionar elemento à seleção",
|
||||
"moveCanvas": "Mover área de desenho",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Pé de coroa (muitos)",
|
||||
"arrowhead_crowfoot_one": "Pé de coroa (um)",
|
||||
"arrowhead_crowfoot_one_or_many": "Pé de coroa (um ou muitos)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Mais opções",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Tipo de seta",
|
||||
"arrowtype_sharp": "Seta retilínea",
|
||||
"arrowtype_round": "Seta curva",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Hiperligação para o objeto",
|
||||
"wrapSelectionInFrame": "Ajustar seleção no quadro",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Hiperligação para o objeto",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Erro"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Guardar no disco",
|
||||
"disk_details": "Exportar os dados da cena para um ficheiro do qual poderá importar mais tarde.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid para Excalidraw",
|
||||
"button": "Inserir",
|
||||
"description": "Atualmente apenas são suportados diagramas <flowchartLink>fluxo</flowchartLink>, <sequenceLink>sequência, </sequenceLink> e <classLink>classe</classLink>. Os outros tipos serão renderizados como imagem no Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Sintaxe Mermaid",
|
||||
"preview": "Pré-visualizar",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Lipire",
|
||||
"pasteAsPlaintext": "Inserare ca text simplu",
|
||||
"pasteCharts": "Lipire diagrame",
|
||||
"chartType_bar": "Diagramă cu bare",
|
||||
"chartType_line": "Diagramă cu linii",
|
||||
"chartType_radar": "Diagramă radar",
|
||||
"chartType_plaintext": "Text simplu",
|
||||
"selectAll": "Selectare totală",
|
||||
"multiSelect": "Adaugă element la selecție",
|
||||
"moveCanvas": "Mutare pânză",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Model entitate-asociere (multe)",
|
||||
"arrowhead_crowfoot_one": "Model entitate-asociere (unul)",
|
||||
"arrowhead_crowfoot_one_or_many": "Model entitate-asociere (unul sau multe)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Mai multe opțiuni",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Tip de săgeată",
|
||||
"arrowtype_sharp": "Săgeată ascuțită",
|
||||
"arrowtype_round": "Săgeată curbată",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Link către obiect",
|
||||
"wrapSelectionInFrame": "Încapsulare selecție în cadru",
|
||||
"tab": "Filă",
|
||||
"shapeSwitch": "Comutare formă"
|
||||
"shapeSwitch": "Comutare formă",
|
||||
"preferences": "Preferințe",
|
||||
"preferences_toolLock": "Blocare instrument",
|
||||
"arrowBinding": "Legare săgeată",
|
||||
"midpointSnapping": "Ancorare la punctele medii"
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Link către obiect",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Eroare"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "În curs de salvare",
|
||||
"defaultMessage": "Se pregătește salvarea..."
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Salvare pe disc",
|
||||
"disk_details": "Exportă datele scenei pe un fișier din care poți importa mai târziu.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid la Excalidraw",
|
||||
"button": "Introducere",
|
||||
"description": "În prezent, numai <flowchartLink>Organigramele</flowchartLink>, <sequenceLink>Diagramele de secvență</sequenceLink> și <classLink>Diagramele de clasă</classLink> sunt acceptate. Celelalte tipuri vor fi redate ca imagine în Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Sintaxă Mermaid",
|
||||
"preview": "Previzualizare",
|
||||
"label": "Mermaid",
|
||||
"inputPlaceholder": "Scrie definiţia diagramei Mermaid aici..."
|
||||
"inputPlaceholder": "Scrie definiţia diagramei Mermaid aici...",
|
||||
"autoFixAvailable": "Este disponibilă funcția de fixare automată"
|
||||
},
|
||||
"ttd": {
|
||||
"error": "Eroare!"
|
||||
@@ -641,7 +661,7 @@
|
||||
"placeholder": {
|
||||
"title": "Hai să-ți proiectăm diagrama",
|
||||
"description": "Descrie diagrama pe care vrei să o creezi și o vom genera pentru tine.",
|
||||
"hint": "În acest moment cunoaștem organigrame, diagrame de secvență și diagrame de clasă."
|
||||
"hint": ""
|
||||
},
|
||||
"preview": "Previzualizare",
|
||||
"insert": "Introducere",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Вставить",
|
||||
"pasteAsPlaintext": "Вставить как обычный текст",
|
||||
"pasteCharts": "Вставить диаграммы",
|
||||
"chartType_bar": "Гистограмма",
|
||||
"chartType_line": "Линейная диаграмма",
|
||||
"chartType_radar": "Лепестковая диаграмма",
|
||||
"chartType_plaintext": "Обычный текст",
|
||||
"selectAll": "Выбрать всё",
|
||||
"multiSelect": "Добавить элемент в выделенный фрагмент",
|
||||
"moveCanvas": "Переместить холст",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Морщинки (много)",
|
||||
"arrowhead_crowfoot_one": "Морщинки (одна)",
|
||||
"arrowhead_crowfoot_one_or_many": "Морщинки (одна или много)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Больше вариантов",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Тип стрелки",
|
||||
"arrowtype_sharp": "Острая стрелка",
|
||||
"arrowtype_round": "Изогнутая стрелка",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Ссылка на объект",
|
||||
"wrapSelectionInFrame": "Поместить выделенное в фрейм",
|
||||
"tab": "Вкладка",
|
||||
"shapeSwitch": "Сменить форму"
|
||||
"shapeSwitch": "Сменить форму",
|
||||
"preferences": "Настройки",
|
||||
"preferences_toolLock": "Закрепить инструмент",
|
||||
"arrowBinding": "Привязка стрелкой",
|
||||
"midpointSnapping": "Привязка к средней точке"
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Ссылка на объект",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Ошибка"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Сохранить на диск",
|
||||
"disk_details": "Экспортировать данные сцены в файл, из которого можно импортировать позже.",
|
||||
@@ -557,9 +576,9 @@
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_line2": "",
|
||||
"center_heading_line3": "",
|
||||
"center_heading": "Ваши рисунки сохраняются в памяти вашего браузера.",
|
||||
"center_heading_line2": "Хранилище браузера может быть очищено неожиданно.",
|
||||
"center_heading_line3": "Сохраните свою работу в файл регулярно, чтобы избежать потери файла.",
|
||||
"center_heading_plus": "Хотите перейти на Excalidraw+?",
|
||||
"menuHint": "Экспорт, настройки, языки, ..."
|
||||
},
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Из Mermaid в Excalidraw",
|
||||
"button": "Вставить",
|
||||
"description": "В настоящее время поддерживаются только <flowchartLink>блок-схемы</flowchartLink>, <sequenceLink>диаграммы последовательности</sequenceLink> и <classLink>диаграммы классов</classLink>. Другие типы будут отображаться в виде изображения в Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Синтаксис Mermaid",
|
||||
"preview": "Предпросмотр",
|
||||
"label": "Mermaid",
|
||||
"inputPlaceholder": "Напишите здесь определение диаграммы Mermaid..."
|
||||
"inputPlaceholder": "Напишите здесь определение диаграммы Mermaid...",
|
||||
"autoFixAvailable": "Автоисправление доступно"
|
||||
},
|
||||
"ttd": {
|
||||
"error": "Ошибка!"
|
||||
@@ -624,7 +644,7 @@
|
||||
"chat": {
|
||||
"inputPlaceholder": "Начните вводить сюда идею диаграммы... ({{shortcut}} для новой строки)",
|
||||
"inputPlaceholderWithMessages": "Продолжайте доработку диаграммы...",
|
||||
"generating": "",
|
||||
"generating": "Генерация...",
|
||||
"rateLimitRemaining": "Осталось запросов сегодня: {{count}}",
|
||||
"role": {
|
||||
"user": "Вы",
|
||||
@@ -641,7 +661,7 @@
|
||||
"placeholder": {
|
||||
"title": "Давайте создадим вашу диаграмму",
|
||||
"description": "Опишите диаграмму, которую вы хотите создать, и мы сгенерируем её для вас.",
|
||||
"hint": "На данный момент мы знаем диаграммы блок-схемы, последовательности и классов."
|
||||
"hint": ""
|
||||
},
|
||||
"preview": "Предпросмотр",
|
||||
"insert": "Вставить",
|
||||
@@ -655,7 +675,7 @@
|
||||
"aiRepair": "Перегенерировать (автоисправление) →",
|
||||
"requestAborted": "Запрос отменён",
|
||||
"requestFailed": "Запрос не удался",
|
||||
"mermaidParseError": ""
|
||||
"mermaidParseError": "Синтаксическая ошибка Mermaid"
|
||||
},
|
||||
"rateLimit": {
|
||||
"messageLimit": "Вы достигли лимита ИИ на бесплатном тарифном плане. Попробуйте Excalidraw+ или вернитесь завтра.",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "අලවන්න",
|
||||
"pasteAsPlaintext": "සරල පෙළ ලෙස අලවන්න",
|
||||
"pasteCharts": "ප්රස්ථාරය",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "සියල්ලම",
|
||||
"multiSelect": "තෝරා ගැනීමට අංගය එකතු කරන්න",
|
||||
"moveCanvas": "කැන්වසය චලනය කරන්න",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "දෝෂ සංවාද මාතෘකාව"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "තැටි මාතෘකාව",
|
||||
"disk_details": "තැටි විස්තර",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid මාතෘකාව",
|
||||
"button": "Mermaid බොත්තම",
|
||||
"description": "Mermaid විස්තර",
|
||||
"description": "",
|
||||
"syntax": "Mermaid වාක්ය ඛණ්ඩය",
|
||||
"preview": "Mermaid පූර්වදර්ශනය",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Vložiť",
|
||||
"pasteAsPlaintext": "Vložiť ako obyčajný text",
|
||||
"pasteCharts": "Vložiť grafy",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Vybrať všetko",
|
||||
"multiSelect": "Pridať prvok do výberu",
|
||||
"moveCanvas": "Pohyb plátna",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Crow's foot (viacero)",
|
||||
"arrowhead_crowfoot_one": "Crow's foot (jedna)",
|
||||
"arrowhead_crowfoot_one_or_many": "Crow's foot (jedna k viacero)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Ďalšie možnosti",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Typ šípky",
|
||||
"arrowtype_sharp": "Ostrá šípka",
|
||||
"arrowtype_round": "Zakrivená šípka",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Odkaz na objekt",
|
||||
"wrapSelectionInFrame": "Zabaliť výber do rámu",
|
||||
"tab": "Tab",
|
||||
"shapeSwitch": "Zmeniť tvar"
|
||||
"shapeSwitch": "Zmeniť tvar",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Odkaz na objekt",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Chyba"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Uložiť na disk",
|
||||
"disk_details": "Exportovať údaje scény do súboru, z ktorého môžu byť neskôr importované.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid do Excalidraw",
|
||||
"button": "Vložiť",
|
||||
"description": "Aktuálne sú podporované iba <flowchartLink>vývojové diagramy</flowchartLink>, <sequenceLink>sekvenčné diagramy</sequenceLink> a <classLink>diagramy tried</classLink>. Ostatné typy budú v Excalidraw vykreslené ako obrázky.",
|
||||
"description": "",
|
||||
"syntax": "Mermaid syntax",
|
||||
"preview": "Ukážka",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Prilepi",
|
||||
"pasteAsPlaintext": "Prilepi kot navadno besedilo",
|
||||
"pasteCharts": "Prilepi grafikone",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Izberi vse",
|
||||
"multiSelect": "Dodaj element v izbor",
|
||||
"moveCanvas": "Premakni platno",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Vranja noga (več)",
|
||||
"arrowhead_crowfoot_one": "Vranja noga (ena)",
|
||||
"arrowhead_crowfoot_one_or_many": "Vranja noga (ena ali več)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Več možnosti",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Vrsta puščice",
|
||||
"arrowtype_sharp": "Ostra puščica",
|
||||
"arrowtype_round": "Ukrivljena puščica",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Povezava do objekta",
|
||||
"wrapSelectionInFrame": "Zavij izbor v okvir",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Povezava do objekta",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Napaka"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Shrani na disk",
|
||||
"disk_details": "Izvozite podatke scene v datoteko, iz katere jo lahko pozneje uvozite.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid v Excalidraw",
|
||||
"button": "Vstavi",
|
||||
"description": "Trenutno so podprti samo <flowchartLink>diagrami poteka</flowchartLink>, <sequenceLink>diagrami zaporedij</sequenceLink> in <classLink>Razredni diagrami</classLink>. Druge vrste bodo upodobljene kot slike v Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Sintaksa Mermaid",
|
||||
"preview": "Predogled",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Klistra in",
|
||||
"pasteAsPlaintext": "Klistra som oformaterad text",
|
||||
"pasteCharts": "Klistra in diagram",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Markera alla",
|
||||
"multiSelect": "Lägg till element till markering",
|
||||
"moveCanvas": "Flytta canvas",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Kråkfot (många)",
|
||||
"arrowhead_crowfoot_one": "Kråkfot (en)",
|
||||
"arrowhead_crowfoot_one_or_many": "Kråkfot (en eller många)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Fler alternativ",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Piltyp",
|
||||
"arrowtype_sharp": "Rak",
|
||||
"arrowtype_round": "Böjd",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Länk till objektet",
|
||||
"wrapSelectionInFrame": "Omslut urvaled i ram",
|
||||
"tab": "Flik",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Länk till objektet",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Fel"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Spara till disk",
|
||||
"disk_details": "Exportera skissdata till en fil som du kan importera från senare.",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid till Excalidraw",
|
||||
"button": "Infoga",
|
||||
"description": "För närvarande stöds endast <flowchartLink>Flödesdiagram</flowchartLink>,<sequenceLink> Sekvensdiagram </sequenceLink> och <classLink>Klassdiagram</classLink>. De andra typerna kommer att återges som bild i Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Mermaid-syntax",
|
||||
"preview": "Förhandsgranska",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "ஒட்டு",
|
||||
"pasteAsPlaintext": "அலங்காரமின்றி ஒட்டு",
|
||||
"pasteCharts": "விளக்கப்படங்களை ஒட்டு",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "எல்லாம் தேர்ந்தெடு",
|
||||
"multiSelect": "உறுப்பைத் தெரிவில் சேர்",
|
||||
"moveCanvas": "கித்தானை நகர்த்து",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "கூடுதல் விருப்பங்கள்",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "அம்புக்குறி வகை",
|
||||
"arrowtype_sharp": "கூர்மையான குறி",
|
||||
"arrowtype_round": "வளைந்தக் குறி",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "இணைப்பு",
|
||||
"wrapSelectionInFrame": "தேர்வை வடிவத்தில் சுற்று",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "இணைப்பு",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "பிழை"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "வட்டில் சேமி",
|
||||
"disk_details": "காட்சித் தரவை நீங்கள் பின்னர் இறக்குமதி செய்யக்கூடிய ஒரு கோப்பிற்கு ஏற்றுமதிசெய்க.",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "மெர்மெய்டு தொடரியல்",
|
||||
"preview": "முன்னோட்டம்",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "วาง",
|
||||
"pasteAsPlaintext": "วางโดยไม่มีการจัดรูปแบบ",
|
||||
"pasteCharts": "วางแผนภูมิ",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "แผนภูมิเส้น",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "ข้อความธรรมดา",
|
||||
"selectAll": "เลือกทั้งหมด",
|
||||
"multiSelect": "เพิ่มองค์ประกอบในการเลือก",
|
||||
"moveCanvas": "เคลื่อนย้ายผ้าใบ",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"more_options": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "ตัวเลือกเพิ่มเติม",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "ชนิดลูกศร",
|
||||
"arrowtype_sharp": "ลูกศรแหลม",
|
||||
"arrowtype_round": "ลูกศรโค้ง",
|
||||
@@ -170,8 +181,12 @@
|
||||
"copyElementLink": "คัดลอกลิงค์ไปสู่วัตถุ",
|
||||
"linkToElement": "ลิงค์ไปสู่วัตถุ",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"tab": "แท็บ",
|
||||
"shapeSwitch": "",
|
||||
"preferences": "การตั้งค่า",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "ลิงค์ไปสู่วัตถุ",
|
||||
@@ -183,20 +198,20 @@
|
||||
"hint_emptyLibrary": "",
|
||||
"hint_emptyPrivateLibrary": "",
|
||||
"search": {
|
||||
"inputPlaceholder": "",
|
||||
"inputPlaceholder": "ค้นหาห้องสมุด",
|
||||
"heading": "",
|
||||
"noResults": "",
|
||||
"clearSearch": ""
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"title": "",
|
||||
"noMatch": "",
|
||||
"singleResult": "",
|
||||
"multipleResults": "",
|
||||
"title": "ใช้บนผืนผ้าใบ",
|
||||
"noMatch": "ไม่พบรายการที่ตรงกัน...",
|
||||
"singleResult": "ผลลัพธ์",
|
||||
"multipleResults": "ผลลัพธ์",
|
||||
"placeholder": "ค้นหาข้อความในผืนผ้าใบ",
|
||||
"frames": "",
|
||||
"texts": ""
|
||||
"frames": "กรอบ",
|
||||
"texts": "ข้อความ"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "รีเซ็ทผืนผ้าใบ",
|
||||
@@ -292,7 +307,7 @@
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "การเลือก",
|
||||
"lasso": "",
|
||||
"lasso": "การเลือก Lasso",
|
||||
"image": "แทรกรูปภาพ",
|
||||
"rectangle": "สี่เหลี่ยมผืนผ้า",
|
||||
"diamond": "รูปเพชร",
|
||||
@@ -393,10 +408,14 @@
|
||||
"shareTitle": ""
|
||||
},
|
||||
"errorDialog": {
|
||||
"title": ""
|
||||
"title": "ข้อผิดพลาด"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "",
|
||||
"disk_title": "บันทึกไปยังดิสก์",
|
||||
"disk_details": "",
|
||||
"disk_button": "บันทึกเป็นไฟล์",
|
||||
"link_title": "ลิงก์ที่สามารถแชร์ได้",
|
||||
@@ -413,9 +432,9 @@
|
||||
"deepBoxSelect": "",
|
||||
"createFlowchart": "",
|
||||
"navigateFlowchart": "",
|
||||
"curvedArrow": "",
|
||||
"curvedLine": "",
|
||||
"documentation": "",
|
||||
"curvedArrow": "ลูกศรโค้ง",
|
||||
"curvedLine": "เส้นโค้ง",
|
||||
"documentation": "เอกสารคู่มือ",
|
||||
"doubleClick": "ดับเบิลคลิก",
|
||||
"drag": "ลาก",
|
||||
"editor": "ตัวแก้ไข",
|
||||
@@ -426,7 +445,7 @@
|
||||
"or": "หรือ",
|
||||
"preventBinding": "",
|
||||
"tools": "เครื่องมือ",
|
||||
"shortcuts": "",
|
||||
"shortcuts": "แป้นพิมพ์ลัด",
|
||||
"textFinish": "",
|
||||
"textNewLine": "",
|
||||
"title": "ช่วยเหลือ",
|
||||
@@ -440,15 +459,15 @@
|
||||
"cropFinish": ""
|
||||
},
|
||||
"clearCanvasDialog": {
|
||||
"title": ""
|
||||
"title": "ล้างผืนผ้าใบ"
|
||||
},
|
||||
"publishDialog": {
|
||||
"title": "",
|
||||
"itemName": "",
|
||||
"itemName": "ชื่อสิ่งของ",
|
||||
"authorName": "ชื่อเจ้าของ",
|
||||
"githubUsername": "ชื่อผู้ใช้ GitHub",
|
||||
"twitterUsername": "ชื่อผู้ใช้ Twitter",
|
||||
"libraryName": "",
|
||||
"libraryName": "ชื่อไลบรารี่",
|
||||
"libraryDesc": "",
|
||||
"website": "เว็บไซต์",
|
||||
"placeholder": {
|
||||
@@ -461,7 +480,7 @@
|
||||
},
|
||||
"errors": {
|
||||
"required": "จำเป็น",
|
||||
"website": ""
|
||||
"website": "ป้อน URL ที่ถูกต้อง"
|
||||
},
|
||||
"noteDescription": "",
|
||||
"noteGuidelines": "",
|
||||
@@ -475,7 +494,7 @@
|
||||
"content": ""
|
||||
},
|
||||
"confirmDialog": {
|
||||
"resetLibrary": "",
|
||||
"resetLibrary": "รีเซ็ตคลัง",
|
||||
"removeItemsFromLib": ""
|
||||
},
|
||||
"imageExportDialog": {
|
||||
@@ -571,7 +590,7 @@
|
||||
}
|
||||
},
|
||||
"colorPicker": {
|
||||
"color": "",
|
||||
"color": "สี",
|
||||
"mostUsedCustomColors": "",
|
||||
"colors": "สี",
|
||||
"shades": "รูปร่าง",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "ดูตัวอย่าง",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
@@ -624,32 +644,32 @@
|
||||
"chat": {
|
||||
"inputPlaceholder": "",
|
||||
"inputPlaceholderWithMessages": "",
|
||||
"generating": "",
|
||||
"generating": "กำลังสร้าง...",
|
||||
"rateLimitRemaining": "",
|
||||
"role": {
|
||||
"user": "",
|
||||
"assistant": "",
|
||||
"system": ""
|
||||
"user": "คุณ",
|
||||
"assistant": "ผู้ช่วย AI",
|
||||
"system": "ระบบ"
|
||||
},
|
||||
"aiBeta": "",
|
||||
"label": "",
|
||||
"menu": "",
|
||||
"newChat": "",
|
||||
"deleteChat": "",
|
||||
"deleteMessage": "",
|
||||
"label": "แชท",
|
||||
"menu": "เมนู",
|
||||
"newChat": "แชทใหม่",
|
||||
"deleteChat": "ลบแชท",
|
||||
"deleteMessage": "ลบข้อความ",
|
||||
"viewAsMermaid": "",
|
||||
"placeholder": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"hint": ""
|
||||
},
|
||||
"preview": "",
|
||||
"insert": "",
|
||||
"retry": "",
|
||||
"preview": "ดูตัวอย่าง",
|
||||
"insert": "แทรก",
|
||||
"retry": "ลองซ้ำ",
|
||||
"errors": {
|
||||
"promptTooShort": "",
|
||||
"promptTooLong": "",
|
||||
"generationFailed": "",
|
||||
"generationFailed": "การสร้างล้มเหลว",
|
||||
"invalidDiagram": "",
|
||||
"fixInMermaid": "",
|
||||
"aiRepair": "",
|
||||
@@ -662,7 +682,7 @@
|
||||
"generalRateLimit": "",
|
||||
"messageLimitInputPlaceholder": ""
|
||||
},
|
||||
"upsellBtnLabel": ""
|
||||
"upsellBtnLabel": "อัปเกรดบัญชีเป็นแบบ Plus"
|
||||
},
|
||||
"quickSearch": {
|
||||
"placeholder": "ค้นหาด่วน"
|
||||
@@ -701,15 +721,15 @@
|
||||
"shortcutHint": ""
|
||||
},
|
||||
"keys": {
|
||||
"ctrl": "",
|
||||
"option": "",
|
||||
"cmd": "",
|
||||
"alt": "",
|
||||
"escape": "",
|
||||
"enter": "",
|
||||
"shift": "",
|
||||
"spacebar": "",
|
||||
"delete": "",
|
||||
"ctrl": "Ctrl",
|
||||
"option": "Option",
|
||||
"cmd": "Cmd",
|
||||
"alt": "Alt",
|
||||
"escape": "Esc",
|
||||
"enter": "Enter",
|
||||
"shift": "เลื่อน",
|
||||
"spacebar": "อวกาศ",
|
||||
"delete": "ลบ",
|
||||
"mmb": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Yapıştır",
|
||||
"pasteAsPlaintext": "Düz metin olarak yapıştır",
|
||||
"pasteCharts": "Grafikleri yapıştır",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Tümünü seç",
|
||||
"multiSelect": "Seçime öge ekle",
|
||||
"moveCanvas": "Tuvali taşı",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "Karga ayak (çok)",
|
||||
"arrowhead_crowfoot_one": "Karga ayak (bir)",
|
||||
"arrowhead_crowfoot_one_or_many": "Karga ayak (bir veya çok sayıda)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Daha fazla seçenek",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Ok şekli",
|
||||
"arrowtype_sharp": "Düz ok",
|
||||
"arrowtype_round": "Kavisli ok",
|
||||
@@ -117,7 +128,7 @@
|
||||
"share": "Paylaş",
|
||||
"showStroke": "Kontur için renk seçiciyi göster",
|
||||
"showBackground": "Arka plan renk seçicisini göster",
|
||||
"showFonts": "Yazı tipi seçici",
|
||||
"showFonts": "Yazı tipi seçiciyi göster",
|
||||
"toggleTheme": "Aydınlık/karanlık mod",
|
||||
"theme": "Tema",
|
||||
"personalLib": "Kişisel Kitaplık",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Nesneye bağlantıla",
|
||||
"wrapSelectionInFrame": "Seçimi karede kaydır",
|
||||
"tab": "Sekme",
|
||||
"shapeSwitch": "Şekli başkasıyla değiştir"
|
||||
"shapeSwitch": "Şekli başkasıyla değiştir",
|
||||
"preferences": "Tercihler",
|
||||
"preferences_toolLock": "Aracı Kilitle",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Nesneye bağlantıla",
|
||||
@@ -341,7 +356,7 @@
|
||||
"canvasPanning": "Tuvali taşımak için {{shortcut_1}} kısayolunu basılı tutun, sürüklerken {{shortcut_2}} kısayolunu veya el aracını kullanın",
|
||||
"linearElement": "Birden fazla nokta için tıklayın, tek çizgi için sürükleyin",
|
||||
"arrowTool": "Birden çok nokta başlatmak için tıklayın, tek bir çizgi için sürükleyin. Ok türünü değiştirmek için {{shortcut}} kısayoluna basın.",
|
||||
"arrowBindModifiers": "",
|
||||
"arrowBindModifiers": "Bağlamayı (kenetlenmeyi) devre dışı bırakmak için {{shortcut_1}} tuşuna, sabit bir noktaya bağlamak için {{shortcut_2}} tuşuna basılı tutun.",
|
||||
"freeDraw": "Tıkla ve sürükle, bitirdiğinde serbest bırak",
|
||||
"text": "İpucu: seçme aracıyla herhangi bir yere çift tıklayarak da yazı ekleyebilirsin",
|
||||
"embeddable": "Web sitesi yerleştirmek için sürükle bırak",
|
||||
@@ -349,22 +364,22 @@
|
||||
"text_editing": "Düzenlemeyi bitirmek için {{shortcut_1}} veya {{shortcut_2}} kısayoluna basın",
|
||||
"linearElementMulti": "Bitirmek için son noktaya tıklayın veya {{shortcut_1}} veya {{shortcut_2}} kısayolunu kullanın",
|
||||
"lockAngle": "{{shortcut}} kısayolunu basılı tutarak açıyı kısıtlayabilirsiniz",
|
||||
"resize": "",
|
||||
"resizeImage": "",
|
||||
"rotate": "",
|
||||
"lineEditor_info": "",
|
||||
"lineEditor_line_info": "",
|
||||
"lineEditor_pointSelected": "",
|
||||
"lineEditor_nothingSelected": "",
|
||||
"resize": "Yeniden boyutlandırırken {{shortcut_1}} tuşuna basılı tutarak oranları koruyabilir, merkezden boyutlandırmak için {{shortcut_2}} tuşuna basılı tutabilirsiniz.",
|
||||
"resizeImage": "Serbestçe boyutlandırmak için {{shortcut_1}} tuşunu, merkez odaklı boyutlandırmak için {{shortcut_2}} tuşunu basılı tutun.",
|
||||
"rotate": "Açıları sabitleyerek döndürmek için {{shortcut}} tuşuna basılı tutun.",
|
||||
"lineEditor_info": "Noktaları düzenlemek için {{shortcut_1}} tuşuna basılı tutarak çift tıklayın veya {{shortcut_2}} tuşuna basın.",
|
||||
"lineEditor_line_info": "Noktaları düzenlemek için çift tıklayın veya {{shortcut}} tuşuna basın.",
|
||||
"lineEditor_pointSelected": "Noktaları (veya noktayı) silmek için {{shortcut_1}}, çoğaltmak için {{shortcut_2}} tuşuna basın veya taşımak için sürükleyin.",
|
||||
"lineEditor_nothingSelected": "Düzenlemek için bir nokta seçin (çoklu seçim için {{shortcut_1}} tuşuna basılı tutun) veya yeni noktalar eklemek için {{shortcut_2}} tuşuna basarak tıklayın.",
|
||||
"publishLibrary": "Kendi kitaplığınızı yayınlayın",
|
||||
"bindTextToElement": "",
|
||||
"createFlowchart": "",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": "",
|
||||
"bindTextToElement": "Metin eklemek için {{shortcut}} tuşuna basın",
|
||||
"createFlowchart": "Akış şeması oluşturmak için {{shortcut}} tuşuna basın",
|
||||
"deepBoxSelect": "Derin seçim yapmak ve sürüklemeyi engellemek için {{shortcut}} tuşuna basılı tutun.",
|
||||
"eraserRevert": "Silinmek üzere işaretlenen öğeleri geri yüklemek (veya geri döndürmek) için {{shortcut}} tuşuna basılı tutun.",
|
||||
"firefox_clipboard_write": "Bu özelliği etkinleştirmek muhtemelen \"dom.events.asyncClipboard.clipboardItem\" bayrağını \"true\" olarak ayarlayarak yapılabilir. Firefox'ta tarayıcı bayraklarını değiştirmek için \"about:config\" sayfasını ziyaret edin.",
|
||||
"disableSnapping": "",
|
||||
"enterCropEditor": "",
|
||||
"leaveCropEditor": ""
|
||||
"disableSnapping": "Hizalamayı (kenetlenmeyi) devre dışı bırakmak için {{shortcut}} tuşuna basılı tutun.",
|
||||
"enterCropEditor": "Görseli kırpmak için üzerine çift tıklayın veya {{shortcut}} tuşuna basın.",
|
||||
"leaveCropEditor": "Kırpmayı tamamlamak için görselin dışına tıklayın veya {{shortcut_1}} ya da {{shortcut_2}} tuşuna basın."
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Önizleme gösterilemiyor",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Hata"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Belleğe kaydet",
|
||||
"disk_details": "Sahne verilerini daha sonra içe aktarabileceğiniz bir dosyaya aktarın.",
|
||||
@@ -436,8 +455,8 @@
|
||||
"toggleElementLock": "Seçimi Kilitle/çöz",
|
||||
"movePageUpDown": "Sayfayı yukarı/aşağı kaydır",
|
||||
"movePageLeftRight": "Sayfayı sola/sağa kaydır",
|
||||
"cropStart": "",
|
||||
"cropFinish": ""
|
||||
"cropStart": "Görseli kırp",
|
||||
"cropFinish": "Görsel kırpmayı tamamla"
|
||||
},
|
||||
"clearCanvasDialog": {
|
||||
"title": "Tuvali temizle"
|
||||
@@ -528,7 +547,7 @@
|
||||
"copyStyles": "Stiller kopyalandı.",
|
||||
"copyToClipboard": "Panoya kopyalandı.",
|
||||
"copyToClipboardAsPng": "{{exportSelection}} panoya PNG olarak\n({{exportColorScheme}}) kopyalandı",
|
||||
"copyToClipboardAsSvg": "",
|
||||
"copyToClipboardAsSvg": "{{exportSelection}} SVG olarak panoya kopyalandı ({{exportColorScheme}})",
|
||||
"fileSaved": "Dosya kaydedildi.",
|
||||
"fileSavedToFilename": "{filename} kaydedildi",
|
||||
"canvas": "tuval",
|
||||
@@ -536,7 +555,7 @@
|
||||
"pasteAsSingleElement": "Tekil obje olarak yapıştırmak için veya var olan bir metin editörüne yapıştırmak için {{shortcut}} kullanın",
|
||||
"unableToEmbed": "Bu URL'i yerleştirmeye şu anda izin verilmiyor. URL'in beyaz listeye alınmasını istiyorsanız GitHub'da bir issue açın",
|
||||
"unrecognizedLinkFormat": "Yerleştirdiğiniz bağlantı beklenen biçime uymuyor. Lütfen kaynak sitenin sağladığı \"yerleştir\" dizesini yapıştırmayı deneyin",
|
||||
"elementLinkCopied": ""
|
||||
"elementLinkCopied": "Bağlantı panoya kopyalandı"
|
||||
},
|
||||
"colors": {
|
||||
"transparent": "Şeffaf",
|
||||
@@ -557,9 +576,9 @@
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_line2": "",
|
||||
"center_heading_line3": "",
|
||||
"center_heading": "Çizimleriniz tarayıcınızın depolama alanına kaydedilir.",
|
||||
"center_heading_line2": "Tarayıcı depolama alanı beklenmedik bir şekilde silinebilir.",
|
||||
"center_heading_line3": "Veri kaybını önlemek için çalışmanızı düzenli olarak bir dosyaya kaydedin.",
|
||||
"center_heading_plus": "Ecalidraw+'a mı gitmek istediniz?",
|
||||
"menuHint": "Dışa aktar, seçenekler, diller, ..."
|
||||
},
|
||||
@@ -571,7 +590,7 @@
|
||||
}
|
||||
},
|
||||
"colorPicker": {
|
||||
"color": "",
|
||||
"color": "Renk",
|
||||
"mostUsedCustomColors": "En çok kullanılan özel renkler",
|
||||
"colors": "Renkler",
|
||||
"shades": "Tonlar",
|
||||
@@ -612,40 +631,41 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid'den Excalidraw'a",
|
||||
"button": "Ekle",
|
||||
"description": "Şu anda yalnızca <flowchartLink>Akış şeması</flowchartLink>,<sequenceLink> Dizi, </sequenceLink> ve <classLink>Sınıf </classLink>Diyagramları deskteklenmektedir. Diğer türler, Excalidraw'da görsel olarak çizilecektir.",
|
||||
"description": "",
|
||||
"syntax": "Mermaid Sözdizimi",
|
||||
"preview": "Önizleme",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"label": "Mermaid",
|
||||
"inputPlaceholder": "\"Mermaid diyagram tanımını buraya yazın...",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
"error": "Hata!"
|
||||
},
|
||||
"chat": {
|
||||
"inputPlaceholder": "",
|
||||
"inputPlaceholderWithMessages": "",
|
||||
"generating": "",
|
||||
"rateLimitRemaining": "",
|
||||
"inputPlaceholder": "Diyagram fikrinizi buraya yazmaya başlayın... (Yeni satır için {{shortcut}})",
|
||||
"inputPlaceholderWithMessages": "Diyagramınızı geliştirmeye devam edin...",
|
||||
"generating": "Oluşturuluyor...",
|
||||
"rateLimitRemaining": "Bugün için {{count}} hak kaldı",
|
||||
"role": {
|
||||
"user": "",
|
||||
"assistant": "",
|
||||
"system": ""
|
||||
"user": "Siz",
|
||||
"assistant": "YZ Asistanı",
|
||||
"system": "Sistem"
|
||||
},
|
||||
"aiBeta": "",
|
||||
"label": "",
|
||||
"menu": "",
|
||||
"newChat": "",
|
||||
"deleteChat": "",
|
||||
"deleteMessage": "",
|
||||
"aiBeta": "YZ Beta",
|
||||
"label": "Sohbet",
|
||||
"menu": "Menü",
|
||||
"newChat": "Yeni Sohbet",
|
||||
"deleteChat": "Sohbeti sil",
|
||||
"deleteMessage": "Mesajı sil",
|
||||
"viewAsMermaid": "",
|
||||
"placeholder": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "\"Hadi, diyagramınızı tasarlayalım",
|
||||
"description": "Oluşturmak istediğiniz diyagramı tarif edin, sizin için biz hazırlayalım.",
|
||||
"hint": ""
|
||||
},
|
||||
"preview": "",
|
||||
"insert": "",
|
||||
"retry": "",
|
||||
"preview": "Önizleme",
|
||||
"insert": "Ekle",
|
||||
"retry": "Yeniden dene",
|
||||
"errors": {
|
||||
"promptTooShort": "",
|
||||
"promptTooLong": "",
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Вставити",
|
||||
"pasteAsPlaintext": "Вставити як текст",
|
||||
"pasteCharts": "Вставити діаграми",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Вибрати все",
|
||||
"multiSelect": "Додати елемент до вибраного",
|
||||
"moveCanvas": "Перемістити полотно",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Інші опції",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Тип стрілки",
|
||||
"arrowtype_sharp": "Гостра стрілка",
|
||||
"arrowtype_round": "Вигнута стрілка",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Посилання на об’єкт",
|
||||
"wrapSelectionInFrame": "Обтікання виділеного фрагмента рамкою",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Посилання на об’єкт",
|
||||
@@ -186,7 +201,7 @@
|
||||
"inputPlaceholder": "",
|
||||
"heading": "",
|
||||
"noResults": "",
|
||||
"clearSearch": ""
|
||||
"clearSearch": "Очистити пошук"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
@@ -234,7 +249,7 @@
|
||||
"clear": "Очистити",
|
||||
"remove": "Видалити",
|
||||
"embed": "Перемкнути вкладення",
|
||||
"publishLibrary": "",
|
||||
"publishLibrary": "Перейменувати або опублікувати",
|
||||
"submit": "Надіслати",
|
||||
"confirm": "Підтвердити",
|
||||
"embeddableInteractionButton": "Натисніть для взаємодії"
|
||||
@@ -357,7 +372,7 @@
|
||||
"lineEditor_pointSelected": "",
|
||||
"lineEditor_nothingSelected": "",
|
||||
"publishLibrary": "Опублікувати свою власну бібліотеку",
|
||||
"bindTextToElement": "",
|
||||
"bindTextToElement": "{{shortcut}} щоб додати текст",
|
||||
"createFlowchart": "",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Помилка"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Зберегти на диск",
|
||||
"disk_details": "Експорт даних сцени в файл, з якого можна імпортувати пізніше.",
|
||||
@@ -571,7 +590,7 @@
|
||||
}
|
||||
},
|
||||
"colorPicker": {
|
||||
"color": "",
|
||||
"color": "Колір",
|
||||
"mostUsedCustomColors": "Найбільш використовувані користувацькі кольори",
|
||||
"colors": "Кольори",
|
||||
"shades": "Тіні",
|
||||
@@ -612,31 +631,32 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid у Excalidraw",
|
||||
"button": "Вставити",
|
||||
"description": "Наразі підтримується тільки <flowchartLink>блок-схеми</flowchartLink><sequenceLink>діаграми послідовностей</sequenceLink> та <classLink>діаграми класів</classLink>. Інші типи будуть відображатися як зображення в Excalidraw.",
|
||||
"description": "",
|
||||
"syntax": "Синтаксис Mermaid",
|
||||
"preview": "Попередній перегляд",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
"error": "Помилка!"
|
||||
},
|
||||
"chat": {
|
||||
"inputPlaceholder": "",
|
||||
"inputPlaceholderWithMessages": "",
|
||||
"generating": "",
|
||||
"rateLimitRemaining": "",
|
||||
"rateLimitRemaining": "Сьогодні залишилося {{count}} запитів",
|
||||
"role": {
|
||||
"user": "",
|
||||
"assistant": "",
|
||||
"system": ""
|
||||
"user": "Ви",
|
||||
"assistant": "ШІ-асистент",
|
||||
"system": "Система"
|
||||
},
|
||||
"aiBeta": "",
|
||||
"label": "",
|
||||
"menu": "",
|
||||
"newChat": "",
|
||||
"deleteChat": "",
|
||||
"deleteMessage": "",
|
||||
"label": "Чат",
|
||||
"menu": "Меню",
|
||||
"newChat": "Новий чат",
|
||||
"deleteChat": "Видалити чат",
|
||||
"deleteMessage": "Видалити повідомлення",
|
||||
"viewAsMermaid": "",
|
||||
"placeholder": {
|
||||
"title": "",
|
||||
@@ -644,10 +664,10 @@
|
||||
"hint": ""
|
||||
},
|
||||
"preview": "",
|
||||
"insert": "",
|
||||
"retry": "",
|
||||
"insert": "Вставити",
|
||||
"retry": "Спробувати знову",
|
||||
"errors": {
|
||||
"promptTooShort": "",
|
||||
"promptTooShort": "Запит надто короткий (мін. {{min}} символів)",
|
||||
"promptTooLong": "",
|
||||
"generationFailed": "",
|
||||
"invalidDiagram": "",
|
||||
@@ -660,7 +680,7 @@
|
||||
"rateLimit": {
|
||||
"messageLimit": "",
|
||||
"generalRateLimit": "",
|
||||
"messageLimitInputPlaceholder": ""
|
||||
"messageLimitInputPlaceholder": "Ви досягли вашого ліміту повідомлень"
|
||||
},
|
||||
"upsellBtnLabel": ""
|
||||
},
|
||||
@@ -701,15 +721,15 @@
|
||||
"shortcutHint": "Для панелі команд використовуйте {{shortcut}}"
|
||||
},
|
||||
"keys": {
|
||||
"ctrl": "",
|
||||
"option": "",
|
||||
"cmd": "",
|
||||
"alt": "",
|
||||
"escape": "",
|
||||
"enter": "",
|
||||
"shift": "",
|
||||
"spacebar": "",
|
||||
"delete": "",
|
||||
"mmb": ""
|
||||
"ctrl": "Ctrl",
|
||||
"option": "Option",
|
||||
"cmd": "Cmd",
|
||||
"alt": "Alt",
|
||||
"escape": "Esc",
|
||||
"enter": "Enter",
|
||||
"shift": "Shift",
|
||||
"spacebar": "Space",
|
||||
"delete": "Delete",
|
||||
"mmb": "Коліщатко"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "",
|
||||
"pasteAsPlaintext": "",
|
||||
"pasteCharts": "",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "",
|
||||
"multiSelect": "",
|
||||
"moveCanvas": "",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": ""
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "",
|
||||
"disk_details": "",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "Dán",
|
||||
"pasteAsPlaintext": "Dán kiểu văn bản thuần",
|
||||
"pasteCharts": "Dán biểu đồ",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "Chọn tất cả",
|
||||
"multiSelect": "Chọn thêm đối tượng này",
|
||||
"moveCanvas": "Di chuyển canvas",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "Tùy chọn khác",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "Loại mũi tên",
|
||||
"arrowtype_sharp": "Mũi tên nhọn",
|
||||
"arrowtype_round": "Mũi tên cong",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "Liên kết đến đối tượng",
|
||||
"wrapSelectionInFrame": "Đưa vùng chọn vào khung",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "Liên kết đến đối tượng",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "Lỗi"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "Lưu về máy",
|
||||
"disk_details": "",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "粘贴",
|
||||
"pasteAsPlaintext": "粘贴为纯文本",
|
||||
"pasteCharts": "粘贴图表",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "全部选中",
|
||||
"multiSelect": "添加元素到选区",
|
||||
"moveCanvas": "移动画布",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "鸦爪标记(多)",
|
||||
"arrowhead_crowfoot_one": "鸦爪标记(一)",
|
||||
"arrowhead_crowfoot_one_or_many": "鸦爪标记(一或多)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "更多选项",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "箭头类型",
|
||||
"arrowtype_sharp": "直箭头",
|
||||
"arrowtype_round": "曲线箭头",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "链接到对象",
|
||||
"wrapSelectionInFrame": "用选中的元素创建画框",
|
||||
"tab": "Tab",
|
||||
"shapeSwitch": "切换形状"
|
||||
"shapeSwitch": "切换形状",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "链接到对象",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "错误"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "保存到本地",
|
||||
"disk_details": "将画布数据导出为文件,以便以后导入",
|
||||
@@ -612,11 +631,12 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid 至 Excalidraw",
|
||||
"button": "插入",
|
||||
"description": "目前仅支持<flowchartLink>流程图</flowchartLink>、<sequenceLink>序列图</sequenceLink>和<classLink>类图</classLink>。其他类型在 Excalidraw 中将以图像呈现。",
|
||||
"description": "",
|
||||
"syntax": "Mermaid 语法",
|
||||
"preview": "预览",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"label": "Mermaid",
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
@@ -653,16 +673,16 @@
|
||||
"invalidDiagram": "",
|
||||
"fixInMermaid": "",
|
||||
"aiRepair": "",
|
||||
"requestAborted": "",
|
||||
"requestFailed": "",
|
||||
"mermaidParseError": ""
|
||||
"requestAborted": "请求中止",
|
||||
"requestFailed": "请求失败",
|
||||
"mermaidParseError": "Mermaid 语法错误"
|
||||
},
|
||||
"rateLimit": {
|
||||
"messageLimit": "",
|
||||
"generalRateLimit": "",
|
||||
"messageLimitInputPlaceholder": ""
|
||||
"messageLimit": "您已达到免费版的 AI 使用上限。请尝试 Excalidraw+ 获取更多额度,或明天再试。",
|
||||
"generalRateLimit": "慢点慢点,您的手速太快了!请稍候再试。",
|
||||
"messageLimitInputPlaceholder": "您已达到消息上限"
|
||||
},
|
||||
"upsellBtnLabel": ""
|
||||
"upsellBtnLabel": "升级到 Plus"
|
||||
},
|
||||
"quickSearch": {
|
||||
"placeholder": "快速搜索"
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "貼上",
|
||||
"pasteAsPlaintext": "",
|
||||
"pasteCharts": "貼上圖表",
|
||||
"chartType_bar": "",
|
||||
"chartType_line": "",
|
||||
"chartType_radar": "",
|
||||
"chartType_plaintext": "",
|
||||
"selectAll": "全選",
|
||||
"multiSelect": "多重選取",
|
||||
"moveCanvas": "移動畫布",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "",
|
||||
"arrowhead_crowfoot_one": "",
|
||||
"arrowhead_crowfoot_one_or_many": "",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "",
|
||||
"arrowtype_sharp": "",
|
||||
"arrowtype_round": "",
|
||||
@@ -171,7 +182,11 @@
|
||||
"linkToElement": "",
|
||||
"wrapSelectionInFrame": "",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"shapeSwitch": "",
|
||||
"preferences": "",
|
||||
"preferences_toolLock": "",
|
||||
"arrowBinding": "",
|
||||
"midpointSnapping": ""
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": ""
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "",
|
||||
"defaultMessage": ""
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "",
|
||||
"disk_details": "",
|
||||
@@ -616,7 +635,8 @@
|
||||
"syntax": "",
|
||||
"preview": "",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"inputPlaceholder": "",
|
||||
"autoFixAvailable": ""
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
"paste": "貼上",
|
||||
"pasteAsPlaintext": "以純文字貼上",
|
||||
"pasteCharts": "貼上圖表",
|
||||
"chartType_bar": "長條圖",
|
||||
"chartType_line": "折線圖",
|
||||
"chartType_radar": "雷達圖",
|
||||
"chartType_plaintext": "純文字",
|
||||
"selectAll": "全選",
|
||||
"multiSelect": "將物件加入選取範圍",
|
||||
"moveCanvas": "移動畫布",
|
||||
@@ -49,7 +53,14 @@
|
||||
"arrowhead_crowfoot_many": "雞爪符號(多)",
|
||||
"arrowhead_crowfoot_one": "雞爪符號(一)",
|
||||
"arrowhead_crowfoot_one_or_many": "雞爪符號(一或多)",
|
||||
"arrowhead_cardinality_one": "",
|
||||
"arrowhead_cardinality_many": "",
|
||||
"arrowhead_cardinality_one_or_many": "",
|
||||
"arrowhead_cardinality_exactly_one": "",
|
||||
"arrowhead_cardinality_zero_or_one": "",
|
||||
"arrowhead_cardinality_zero_or_many": "",
|
||||
"more_options": "更多選項",
|
||||
"cardinality": "",
|
||||
"arrowtypes": "箭頭類型",
|
||||
"arrowtype_sharp": "尖銳箭頭",
|
||||
"arrowtype_round": "彎角箭頭",
|
||||
@@ -142,8 +153,8 @@
|
||||
"editArrow": "編輯箭頭"
|
||||
},
|
||||
"polygon": {
|
||||
"breakPolygon": "",
|
||||
"convertToPolygon": ""
|
||||
"breakPolygon": "解除多邊形",
|
||||
"convertToPolygon": "轉換為多邊形"
|
||||
},
|
||||
"elementLock": {
|
||||
"lock": "鎖定",
|
||||
@@ -170,8 +181,12 @@
|
||||
"copyElementLink": "複製此物件的連結",
|
||||
"linkToElement": "連結至物件",
|
||||
"wrapSelectionInFrame": "將選取內容放入框架",
|
||||
"tab": "",
|
||||
"shapeSwitch": ""
|
||||
"tab": "Tab",
|
||||
"shapeSwitch": "切換形狀",
|
||||
"preferences": "偏好設定",
|
||||
"preferences_toolLock": "鎖定工具",
|
||||
"arrowBinding": "箭頭連結",
|
||||
"midpointSnapping": "貼齊中點"
|
||||
},
|
||||
"elementLink": {
|
||||
"title": "連結至物件",
|
||||
@@ -183,10 +198,10 @@
|
||||
"hint_emptyLibrary": "選取畫布上的物件以加入,或從下方的公開儲存庫中安裝資料庫。",
|
||||
"hint_emptyPrivateLibrary": "選擇畫布上的物件以在此加入。",
|
||||
"search": {
|
||||
"inputPlaceholder": "",
|
||||
"heading": "",
|
||||
"noResults": "",
|
||||
"clearSearch": ""
|
||||
"inputPlaceholder": "搜尋資料庫",
|
||||
"heading": "相符的資料庫",
|
||||
"noResults": "找不到相符的項目...",
|
||||
"clearSearch": "清除搜尋"
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
@@ -195,8 +210,8 @@
|
||||
"singleResult": "結果",
|
||||
"multipleResults": "結果",
|
||||
"placeholder": "在畫布上尋找文字…",
|
||||
"frames": "",
|
||||
"texts": ""
|
||||
"frames": "框架",
|
||||
"texts": "文字"
|
||||
},
|
||||
"buttons": {
|
||||
"clearReset": "重設畫布",
|
||||
@@ -230,11 +245,11 @@
|
||||
"objectsSnapMode": "吸附至物件",
|
||||
"exitZenMode": "離開專注模式",
|
||||
"cancel": "取消",
|
||||
"saveLibNames": "",
|
||||
"saveLibNames": "儲存名稱並離開",
|
||||
"clear": "清除",
|
||||
"remove": "刪除",
|
||||
"embed": "切換嵌入",
|
||||
"publishLibrary": "",
|
||||
"publishLibrary": "重新命名或發布",
|
||||
"submit": "送出",
|
||||
"confirm": "確認",
|
||||
"embeddableInteractionButton": "點擊以互動"
|
||||
@@ -261,7 +276,7 @@
|
||||
"removeItemsFromsLibrary": "從資料庫刪除 {{count}} 項?",
|
||||
"invalidEncryptionKey": "加密鍵必須為22字元。即時協作已停用。",
|
||||
"collabOfflineWarning": "沒有可用的網路連線。\n變更無法儲存!",
|
||||
"localStorageQuotaExceeded": ""
|
||||
"localStorageQuotaExceeded": "瀏覽器儲存空間已滿。變更將不會被儲存。"
|
||||
},
|
||||
"errors": {
|
||||
"unsupportedFileType": "不支援的檔案類型。",
|
||||
@@ -292,7 +307,7 @@
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "選取",
|
||||
"lasso": "",
|
||||
"lasso": "繩索選取",
|
||||
"image": "插入圖片",
|
||||
"rectangle": "長方形",
|
||||
"diamond": "菱形",
|
||||
@@ -313,7 +328,7 @@
|
||||
"hand": "手形(平移工具)",
|
||||
"extraTools": "更多工具",
|
||||
"mermaidToExcalidraw": "Mermaid 至 Excalidraw",
|
||||
"convertElementType": ""
|
||||
"convertElementType": "切換形狀類型"
|
||||
},
|
||||
"element": {
|
||||
"rectangle": "矩形",
|
||||
@@ -337,34 +352,34 @@
|
||||
"shapes": "形狀"
|
||||
},
|
||||
"hints": {
|
||||
"dismissSearch": "",
|
||||
"canvasPanning": "",
|
||||
"dismissSearch": "{{shortcut}} 以隱藏搜尋",
|
||||
"canvasPanning": "如要移動畫布,請在拖曳時按住 {{shortcut_1}} 或 {{shortcut_2}},或使用手形工具",
|
||||
"linearElement": "點擊以繪製多點曲線;或拖曳以繪製直線",
|
||||
"arrowTool": "",
|
||||
"arrowBindModifiers": "",
|
||||
"arrowTool": "點選以開始建立多點,拖曳可建立單一線段。再次按下 {{shortcut}} 以變更箭頭類型。",
|
||||
"arrowBindModifiers": "按住 {{shortcut_1}} 以停用綁定,或按住 {{shortcut_2}} 以綁定於固定點",
|
||||
"freeDraw": "點擊並拖曳來繪圖,放開即結束",
|
||||
"text": "提示:亦可使用選取工具在任何地方雙擊來加入文字",
|
||||
"embeddable": "點擊並拖移以建立嵌入網站",
|
||||
"text_selected": "",
|
||||
"text_editing": "",
|
||||
"linearElementMulti": "",
|
||||
"lockAngle": "",
|
||||
"resize": "",
|
||||
"resizeImage": "",
|
||||
"rotate": "",
|
||||
"lineEditor_info": "",
|
||||
"lineEditor_line_info": "",
|
||||
"lineEditor_pointSelected": "",
|
||||
"lineEditor_nothingSelected": "",
|
||||
"text_selected": "按兩下滑鼠或按 {{shortcut}} 以編輯文字",
|
||||
"text_editing": "{{shortcut_1}} 或 {{shortcut_2}} 以結束編輯",
|
||||
"linearElementMulti": "點選最後一個點,或按下 {{shortcut_1}} 或 {{shortcut_2}} 以完成",
|
||||
"lockAngle": "你可以按住 {{shortcut}} 以鎖定角度",
|
||||
"resize": "縮放時按住 {{shortcut_1}} 可保持等比例,\n按住 {{shortcut_2}} 則可從中心縮放",
|
||||
"resizeImage": "按住 {{shortcut_1}} 可自由縮放,\n按住 {{shortcut_2}} 則可從中心縮放",
|
||||
"rotate": "你可以在旋轉時按住 {{shortcut}} 以鎖定角度",
|
||||
"lineEditor_info": "按住 {{shortcut_1}} 並按兩下或按下 {{shortcut_2}} 以編輯點",
|
||||
"lineEditor_line_info": "按兩下或按 {{shortcut}} 以編輯點",
|
||||
"lineEditor_pointSelected": "按下 {{shortcut_1}} 移除點,\n{{shortcut_2}} 重製,或拖曳以移動",
|
||||
"lineEditor_nothingSelected": "選取一個點以進行編輯(按住 {{shortcut_1}} 可多選),\n或按住 {{shortcut_2}} 並點選以新增點",
|
||||
"publishLibrary": "發布個人資料庫",
|
||||
"bindTextToElement": "",
|
||||
"createFlowchart": "",
|
||||
"deepBoxSelect": "",
|
||||
"eraserRevert": "",
|
||||
"bindTextToElement": "按下 {{shortcut}} 新增文字",
|
||||
"createFlowchart": "按下 {{shortcut}} 建立流程圖",
|
||||
"deepBoxSelect": "按住 {{shortcut}} 以進行深度選取,並防止拖曳",
|
||||
"eraserRevert": "按住 {{shortcut}} 以還原標記為刪除的元素",
|
||||
"firefox_clipboard_write": "此功能有機會透過將 \"dom.events.asyncClipboard.clipboardItem\" 設定為 \"true\" 來開啟。\n若要變更 Firefox 瀏覽器的此設定值,請至 \"about:config\" 頁面。",
|
||||
"disableSnapping": "",
|
||||
"enterCropEditor": "",
|
||||
"leaveCropEditor": ""
|
||||
"disableSnapping": "按住 {{shortcut}} 以停用貼齊(自動對齊)",
|
||||
"enterCropEditor": "按兩下圖片或按下 {{shortcut}} 以裁剪圖片",
|
||||
"leaveCropEditor": "點選圖片外部,或按下 {{shortcut_1}} 或 {{shortcut_2}} 以完成裁剪"
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "無法顯示預覽",
|
||||
@@ -395,6 +410,10 @@
|
||||
"errorDialog": {
|
||||
"title": "錯誤"
|
||||
},
|
||||
"progressDialog": {
|
||||
"title": "儲存中",
|
||||
"defaultMessage": "正在準備儲存..."
|
||||
},
|
||||
"exportDialog": {
|
||||
"disk_title": "儲存至硬碟",
|
||||
"disk_details": "將場景匯出為可供匯入之檔案",
|
||||
@@ -557,9 +576,9 @@
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_line2": "",
|
||||
"center_heading_line3": "",
|
||||
"center_heading": "你的繪圖已儲存在瀏覽器的儲存空間中。",
|
||||
"center_heading_line2": "瀏覽器儲存空間可能會意外地被清除。",
|
||||
"center_heading_line3": "請定期將作品儲存為檔案,以避免遺失。",
|
||||
"center_heading_plus": "您是否是要前往 Excalidraw+ ?",
|
||||
"menuHint": "輸出、偏好設定、語言..."
|
||||
},
|
||||
@@ -571,7 +590,7 @@
|
||||
}
|
||||
},
|
||||
"colorPicker": {
|
||||
"color": "",
|
||||
"color": "顏色",
|
||||
"mostUsedCustomColors": "最常使用的自訂顏色",
|
||||
"colors": "顏色",
|
||||
"shades": "漸變色",
|
||||
@@ -612,57 +631,58 @@
|
||||
"mermaid": {
|
||||
"title": "Mermaid 至 Excalidraw",
|
||||
"button": "插入",
|
||||
"description": "目前僅支援 <flowchartLink>Flowchart</flowchartLink> 、 <sequenceLink>Sequence</sequenceLink> 及 <classLink>Class </classLink> 圖表。其餘檔案類型在 Excalidraw 將會以圖像呈現。",
|
||||
"description": "",
|
||||
"syntax": "Mermaid 語法",
|
||||
"preview": "預覽",
|
||||
"label": "",
|
||||
"inputPlaceholder": ""
|
||||
"label": "Mermaid",
|
||||
"inputPlaceholder": "在這裡編寫 Mermaid 定義...",
|
||||
"autoFixAvailable": "自動修正可用"
|
||||
},
|
||||
"ttd": {
|
||||
"error": ""
|
||||
"error": "錯誤!"
|
||||
},
|
||||
"chat": {
|
||||
"inputPlaceholder": "",
|
||||
"inputPlaceholderWithMessages": "",
|
||||
"generating": "",
|
||||
"rateLimitRemaining": "",
|
||||
"inputPlaceholder": "在這裡開始輸入你的圖表構想...({{shortcut}} 換行)",
|
||||
"inputPlaceholderWithMessages": "繼續調整你的圖表...",
|
||||
"generating": "生成中...",
|
||||
"rateLimitRemaining": "今日剩餘 {{count}} 次請求",
|
||||
"role": {
|
||||
"user": "",
|
||||
"assistant": "",
|
||||
"system": ""
|
||||
"user": "你",
|
||||
"assistant": "AI 助理",
|
||||
"system": "系統"
|
||||
},
|
||||
"aiBeta": "",
|
||||
"label": "",
|
||||
"menu": "",
|
||||
"newChat": "",
|
||||
"deleteChat": "",
|
||||
"deleteMessage": "",
|
||||
"viewAsMermaid": "",
|
||||
"aiBeta": "AI Beta 版",
|
||||
"label": "對話",
|
||||
"menu": "選單",
|
||||
"newChat": "新的對話",
|
||||
"deleteChat": "刪除對話",
|
||||
"deleteMessage": "刪除訊息",
|
||||
"viewAsMermaid": "以 Mermaid 格式檢視",
|
||||
"placeholder": {
|
||||
"title": "",
|
||||
"description": "",
|
||||
"title": "讓我們開始設計你的圖表",
|
||||
"description": "描述你想要建立的圖表,我們將為你生成。",
|
||||
"hint": ""
|
||||
},
|
||||
"preview": "",
|
||||
"insert": "",
|
||||
"retry": "",
|
||||
"preview": "預覽",
|
||||
"insert": "插入",
|
||||
"retry": "重試",
|
||||
"errors": {
|
||||
"promptTooShort": "",
|
||||
"promptTooLong": "",
|
||||
"generationFailed": "",
|
||||
"invalidDiagram": "",
|
||||
"fixInMermaid": "",
|
||||
"aiRepair": "",
|
||||
"requestAborted": "",
|
||||
"requestFailed": "",
|
||||
"mermaidParseError": ""
|
||||
"promptTooShort": "提示詞太短(至少 {{min}} 個字元)",
|
||||
"promptTooLong": "提示詞太長(最多 {{max}} 個字元)",
|
||||
"generationFailed": "生成失敗",
|
||||
"invalidDiagram": "生成的圖表語法無效 :(。你可以手動編輯、嘗試自動修復,或換個提示詞試試。",
|
||||
"fixInMermaid": "手動編輯 Mermaid →",
|
||||
"aiRepair": "重新生成(自動修復)→",
|
||||
"requestAborted": "請求已中止",
|
||||
"requestFailed": "請求失敗",
|
||||
"mermaidParseError": "Mermaid 語法錯誤"
|
||||
},
|
||||
"rateLimit": {
|
||||
"messageLimit": "",
|
||||
"generalRateLimit": "",
|
||||
"messageLimitInputPlaceholder": ""
|
||||
"messageLimit": "你已達到免費方案的 AI 使用上限。請試用 Excalidraw+ 以取得更多額度,或明天再試。",
|
||||
"generalRateLimit": "別心急,你的操作速度太快了!請稍等片刻後再試。",
|
||||
"messageLimitInputPlaceholder": "你已達到訊息上限"
|
||||
},
|
||||
"upsellBtnLabel": ""
|
||||
"upsellBtnLabel": "升級至 Plus"
|
||||
},
|
||||
"quickSearch": {
|
||||
"placeholder": "快速搜尋"
|
||||
@@ -701,15 +721,15 @@
|
||||
"shortcutHint": "使用 {{shortcut}} 開啟指令面板"
|
||||
},
|
||||
"keys": {
|
||||
"ctrl": "",
|
||||
"option": "",
|
||||
"cmd": "",
|
||||
"alt": "",
|
||||
"escape": "",
|
||||
"enter": "",
|
||||
"shift": "",
|
||||
"spacebar": "",
|
||||
"delete": "",
|
||||
"mmb": ""
|
||||
"ctrl": "Ctrl",
|
||||
"option": "Option",
|
||||
"cmd": "Cmd",
|
||||
"alt": "Alt",
|
||||
"escape": "Esc",
|
||||
"enter": "Enter",
|
||||
"shift": "Shift",
|
||||
"spacebar": "空白鍵",
|
||||
"delete": "Delete",
|
||||
"mmb": "滾輪"
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user