Clean up camelCase and snake_case in frontend code
This commit is contained in:
parent
71f12db1e6
commit
1a90a4db86
File diff suppressed because one or more lines are too long
|
|
@ -63,9 +63,6 @@ pub const SCROLLBAR_SPACING: f64 = 0.1;
|
||||||
pub const ASYMPTOTIC_EFFECT: f64 = 0.5;
|
pub const ASYMPTOTIC_EFFECT: f64 = 0.5;
|
||||||
pub const SCALE_EFFECT: f64 = 0.5;
|
pub const SCALE_EFFECT: f64 = 0.5;
|
||||||
|
|
||||||
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
|
|
||||||
pub const FILE_SAVE_SUFFIX: &str = ".graphite";
|
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
pub const COLOR_ACCENT: Color = Color::from_unsafe(0x00 as f32 / 255., 0xA8 as f32 / 255., 0xFF as f32 / 255.);
|
pub const COLOR_ACCENT: Color = Color::from_unsafe(0x00 as f32 / 255., 0xA8 as f32 / 255., 0xFF as f32 / 255.);
|
||||||
|
|
||||||
|
|
@ -74,5 +71,8 @@ pub const DEFAULT_FONT_FAMILY: &str = "Merriweather";
|
||||||
pub const DEFAULT_FONT_STYLE: &str = "Normal (400)";
|
pub const DEFAULT_FONT_STYLE: &str = "Normal (400)";
|
||||||
|
|
||||||
// Document
|
// Document
|
||||||
pub const GRAPHITE_DOCUMENT_VERSION: &str = "0.0.11"; // Remember to save a simple document and replace the test file `graphite-test-document.graphite`
|
pub const GRAPHITE_DOCUMENT_VERSION: &str = "0.0.12"; // Remember to save a simple document and replace the test file `graphite-test-document.graphite`
|
||||||
|
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
|
||||||
|
pub const FILE_SAVE_SUFFIX: &str = ".graphite";
|
||||||
|
|
||||||
pub const VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR: f32 = 1.05;
|
pub const VIEWPORT_ZOOM_TO_FIT_PADDING_SCALE_FACTOR: f32 = 1.05;
|
||||||
|
|
|
||||||
|
|
@ -16,50 +16,166 @@ use serde::{Deserialize, Serialize};
|
||||||
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum FrontendMessage {
|
pub enum FrontendMessage {
|
||||||
// Display prefix: make the frontend show something, like a dialog
|
// Display prefix: make the frontend show something, like a dialog
|
||||||
DisplayDialog { icon: String },
|
DisplayDialog {
|
||||||
|
icon: String,
|
||||||
|
},
|
||||||
DisplayDialogDismiss,
|
DisplayDialogDismiss,
|
||||||
DisplayDialogPanic { panic_info: String, header: String, description: String },
|
DisplayDialogPanic {
|
||||||
DisplayEditableTextbox { text: String, line_width: Option<f64>, font_size: f64, color: Color },
|
#[serde(rename = "panicInfo")]
|
||||||
|
panic_info: String,
|
||||||
|
header: String,
|
||||||
|
description: String,
|
||||||
|
},
|
||||||
|
DisplayEditableTextbox {
|
||||||
|
text: String,
|
||||||
|
#[serde(rename = "lineWidth")]
|
||||||
|
line_width: Option<f64>,
|
||||||
|
#[serde(rename = "fontSize")]
|
||||||
|
font_size: f64,
|
||||||
|
color: Color,
|
||||||
|
},
|
||||||
DisplayRemoveEditableTextbox,
|
DisplayRemoveEditableTextbox,
|
||||||
|
|
||||||
// Trigger prefix: cause a browser API to do something
|
// Trigger prefix: cause a browser API to do something
|
||||||
TriggerAboutGraphiteLocalizedCommitDate { commit_date: String },
|
TriggerAboutGraphiteLocalizedCommitDate {
|
||||||
TriggerFileDownload { document: String, name: String },
|
#[serde(rename = "commitDate")]
|
||||||
TriggerFontLoad { font: Font, is_default: bool },
|
commit_date: String,
|
||||||
|
},
|
||||||
|
TriggerFileDownload {
|
||||||
|
document: String,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
TriggerFontLoad {
|
||||||
|
font: Font,
|
||||||
|
#[serde(rename = "isDefault")]
|
||||||
|
is_default: bool,
|
||||||
|
},
|
||||||
TriggerImport,
|
TriggerImport,
|
||||||
TriggerIndexedDbRemoveDocument { document_id: u64 },
|
TriggerIndexedDbRemoveDocument {
|
||||||
TriggerIndexedDbWriteDocument { document: String, details: FrontendDocumentDetails, version: String },
|
#[serde(rename = "documentId")]
|
||||||
|
document_id: u64,
|
||||||
|
},
|
||||||
|
TriggerIndexedDbWriteDocument {
|
||||||
|
document: String,
|
||||||
|
details: FrontendDocumentDetails,
|
||||||
|
version: String,
|
||||||
|
},
|
||||||
TriggerOpenDocument,
|
TriggerOpenDocument,
|
||||||
TriggerPaste,
|
TriggerPaste,
|
||||||
TriggerRasterDownload { document: String, name: String, mime: String, size: (f64, f64) },
|
TriggerRasterDownload {
|
||||||
|
document: String,
|
||||||
|
name: String,
|
||||||
|
mime: String,
|
||||||
|
size: (f64, f64),
|
||||||
|
},
|
||||||
TriggerRefreshBoundsOfViewports,
|
TriggerRefreshBoundsOfViewports,
|
||||||
TriggerTextCommit,
|
TriggerTextCommit,
|
||||||
TriggerTextCopy { copy_text: String },
|
TriggerTextCopy {
|
||||||
|
#[serde(rename = "copyText")]
|
||||||
|
copy_text: String,
|
||||||
|
},
|
||||||
TriggerViewportResize,
|
TriggerViewportResize,
|
||||||
TriggerVisitLink { url: String },
|
TriggerVisitLink {
|
||||||
|
url: String,
|
||||||
|
},
|
||||||
|
|
||||||
// Update prefix: give the frontend a new value or state for it to use
|
// Update prefix: give the frontend a new value or state for it to use
|
||||||
UpdateActiveDocument { document_id: u64 },
|
UpdateActiveDocument {
|
||||||
UpdateDialogDetails { layout_target: LayoutTarget, layout: SubLayout },
|
#[serde(rename = "documentId")]
|
||||||
UpdateDocumentArtboards { svg: String },
|
document_id: u64,
|
||||||
UpdateDocumentArtwork { svg: String },
|
},
|
||||||
UpdateDocumentBarLayout { layout_target: LayoutTarget, layout: SubLayout },
|
UpdateDialogDetails {
|
||||||
UpdateDocumentLayerDetails { data: LayerPanelEntry },
|
#[serde(rename = "layoutTarget")]
|
||||||
UpdateDocumentLayerTreeStructure { data_buffer: RawBuffer },
|
layout_target: LayoutTarget,
|
||||||
UpdateDocumentModeLayout { layout_target: LayoutTarget, layout: SubLayout },
|
layout: SubLayout,
|
||||||
UpdateDocumentOverlays { svg: String },
|
},
|
||||||
UpdateDocumentRulers { origin: (f64, f64), spacing: f64, interval: f64 },
|
UpdateDocumentArtboards {
|
||||||
UpdateDocumentScrollbars { position: (f64, f64), size: (f64, f64), multiplier: (f64, f64) },
|
svg: String,
|
||||||
UpdateImageData { image_data: Vec<FrontendImageData> },
|
},
|
||||||
UpdateInputHints { hint_data: HintData },
|
UpdateDocumentArtwork {
|
||||||
UpdateLayerTreeOptionsLayout { layout_target: LayoutTarget, layout: SubLayout },
|
svg: String,
|
||||||
UpdateMenuBarLayout { layout_target: LayoutTarget, layout: Vec<MenuColumn> },
|
},
|
||||||
UpdateMouseCursor { cursor: MouseCursorIcon },
|
UpdateDocumentBarLayout {
|
||||||
UpdateNodeGraphVisibility { visible: bool },
|
#[serde(rename = "layoutTarget")]
|
||||||
UpdateOpenDocumentsList { open_documents: Vec<FrontendDocumentDetails> },
|
layout_target: LayoutTarget,
|
||||||
UpdatePropertyPanelOptionsLayout { layout_target: LayoutTarget, layout: SubLayout },
|
layout: SubLayout,
|
||||||
UpdatePropertyPanelSectionsLayout { layout_target: LayoutTarget, layout: SubLayout },
|
},
|
||||||
UpdateToolOptionsLayout { layout_target: LayoutTarget, layout: SubLayout },
|
UpdateDocumentLayerDetails {
|
||||||
UpdateToolShelfLayout { layout_target: LayoutTarget, layout: SubLayout },
|
data: LayerPanelEntry,
|
||||||
UpdateWorkingColorsLayout { layout_target: LayoutTarget, layout: SubLayout },
|
},
|
||||||
|
UpdateDocumentLayerTreeStructure {
|
||||||
|
#[serde(rename = "dataBuffer")]
|
||||||
|
data_buffer: RawBuffer,
|
||||||
|
},
|
||||||
|
UpdateDocumentModeLayout {
|
||||||
|
#[serde(rename = "layoutTarget")]
|
||||||
|
layout_target: LayoutTarget,
|
||||||
|
layout: SubLayout,
|
||||||
|
},
|
||||||
|
UpdateDocumentOverlays {
|
||||||
|
svg: String,
|
||||||
|
},
|
||||||
|
UpdateDocumentRulers {
|
||||||
|
origin: (f64, f64),
|
||||||
|
spacing: f64,
|
||||||
|
interval: f64,
|
||||||
|
},
|
||||||
|
UpdateDocumentScrollbars {
|
||||||
|
position: (f64, f64),
|
||||||
|
size: (f64, f64),
|
||||||
|
multiplier: (f64, f64),
|
||||||
|
},
|
||||||
|
UpdateImageData {
|
||||||
|
#[serde(rename = "imageData")]
|
||||||
|
image_data: Vec<FrontendImageData>,
|
||||||
|
},
|
||||||
|
UpdateInputHints {
|
||||||
|
#[serde(rename = "hintData")]
|
||||||
|
hint_data: HintData,
|
||||||
|
},
|
||||||
|
UpdateLayerTreeOptionsLayout {
|
||||||
|
#[serde(rename = "layoutTarget")]
|
||||||
|
layout_target: LayoutTarget,
|
||||||
|
layout: SubLayout,
|
||||||
|
},
|
||||||
|
UpdateMenuBarLayout {
|
||||||
|
#[serde(rename = "layoutTarget")]
|
||||||
|
layout_target: LayoutTarget,
|
||||||
|
layout: Vec<MenuColumn>,
|
||||||
|
},
|
||||||
|
UpdateMouseCursor {
|
||||||
|
cursor: MouseCursorIcon,
|
||||||
|
},
|
||||||
|
UpdateNodeGraphVisibility {
|
||||||
|
visible: bool,
|
||||||
|
},
|
||||||
|
UpdateOpenDocumentsList {
|
||||||
|
#[serde(rename = "openDocuments")]
|
||||||
|
open_documents: Vec<FrontendDocumentDetails>,
|
||||||
|
},
|
||||||
|
UpdatePropertyPanelOptionsLayout {
|
||||||
|
#[serde(rename = "layoutTarget")]
|
||||||
|
layout_target: LayoutTarget,
|
||||||
|
layout: SubLayout,
|
||||||
|
},
|
||||||
|
UpdatePropertyPanelSectionsLayout {
|
||||||
|
#[serde(rename = "layoutTarget")]
|
||||||
|
layout_target: LayoutTarget,
|
||||||
|
layout: SubLayout,
|
||||||
|
},
|
||||||
|
UpdateToolOptionsLayout {
|
||||||
|
#[serde(rename = "layoutTarget")]
|
||||||
|
layout_target: LayoutTarget,
|
||||||
|
layout: SubLayout,
|
||||||
|
},
|
||||||
|
UpdateToolShelfLayout {
|
||||||
|
#[serde(rename = "layoutTarget")]
|
||||||
|
layout_target: LayoutTarget,
|
||||||
|
layout: SubLayout,
|
||||||
|
},
|
||||||
|
UpdateWorkingColorsLayout {
|
||||||
|
#[serde(rename = "layoutTarget")]
|
||||||
|
layout_target: LayoutTarget,
|
||||||
|
layout: SubLayout,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct FrontendDocumentDetails {
|
pub struct FrontendDocumentDetails {
|
||||||
|
#[serde(rename = "isSaved")]
|
||||||
pub is_saved: bool,
|
pub is_saved: bool,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,9 @@ impl LayerMetadata {
|
||||||
pub struct LayerPanelEntry {
|
pub struct LayerPanelEntry {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub visible: bool,
|
pub visible: bool,
|
||||||
|
#[serde(rename = "layerType")]
|
||||||
pub layer_type: LayerDataTypeDiscriminant,
|
pub layer_type: LayerDataTypeDiscriminant,
|
||||||
|
#[serde(rename = "layerMetadata")]
|
||||||
pub layer_metadata: LayerMetadata,
|
pub layer_metadata: LayerMetadata,
|
||||||
pub path: Vec<LayerId>,
|
pub path: Vec<LayerId>,
|
||||||
pub thumbnail: String,
|
pub thumbnail: String,
|
||||||
|
|
|
||||||
|
|
@ -295,7 +295,7 @@ export default defineComponent({
|
||||||
|
|
||||||
// Initialize certain setup tasks required by the editor backend to be ready for the user now that the frontend is ready
|
// Initialize certain setup tasks required by the editor backend to be ready for the user now that the frontend is ready
|
||||||
const platform = operatingSystem();
|
const platform = operatingSystem();
|
||||||
this.editor.instance.init_after_frontend_ready(platform);
|
this.editor.instance.initAfterFrontendReady(platform);
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
// Call the destructor for each manager
|
// Call the destructor for each manager
|
||||||
|
|
|
||||||
|
|
@ -256,27 +256,27 @@ export default defineComponent({
|
||||||
const buffer = await file.arrayBuffer();
|
const buffer = await file.arrayBuffer();
|
||||||
const u8Array = new Uint8Array(buffer);
|
const u8Array = new Uint8Array(buffer);
|
||||||
|
|
||||||
this.editor.instance.paste_image(file.type, u8Array, e.clientX, e.clientY);
|
this.editor.instance.pasteImage(file.type, u8Array, e.clientX, e.clientY);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
translateCanvasX(newValue: number) {
|
translateCanvasX(newValue: number) {
|
||||||
const delta = newValue - this.scrollbarPos.x;
|
const delta = newValue - this.scrollbarPos.x;
|
||||||
this.scrollbarPos.x = newValue;
|
this.scrollbarPos.x = newValue;
|
||||||
this.editor.instance.translate_canvas(-delta * this.scrollbarMultiplier.x, 0);
|
this.editor.instance.translateCanvas(-delta * this.scrollbarMultiplier.x, 0);
|
||||||
},
|
},
|
||||||
translateCanvasY(newValue: number) {
|
translateCanvasY(newValue: number) {
|
||||||
const delta = newValue - this.scrollbarPos.y;
|
const delta = newValue - this.scrollbarPos.y;
|
||||||
this.scrollbarPos.y = newValue;
|
this.scrollbarPos.y = newValue;
|
||||||
this.editor.instance.translate_canvas(0, -delta * this.scrollbarMultiplier.y);
|
this.editor.instance.translateCanvas(0, -delta * this.scrollbarMultiplier.y);
|
||||||
},
|
},
|
||||||
pageX(delta: number) {
|
pageX(delta: number) {
|
||||||
const move = delta < 0 ? 1 : -1;
|
const move = delta < 0 ? 1 : -1;
|
||||||
this.editor.instance.translate_canvas_by_fraction(move, 0);
|
this.editor.instance.translateCanvasByFraction(move, 0);
|
||||||
},
|
},
|
||||||
pageY(delta: number) {
|
pageY(delta: number) {
|
||||||
const move = delta < 0 ? 1 : -1;
|
const move = delta < 0 ? 1 : -1;
|
||||||
this.editor.instance.translate_canvas_by_fraction(0, move);
|
this.editor.instance.translateCanvasByFraction(0, move);
|
||||||
},
|
},
|
||||||
canvasPointerDown(e: PointerEvent) {
|
canvasPointerDown(e: PointerEvent) {
|
||||||
const onEditbox = e.target instanceof HTMLDivElement && e.target.contentEditable;
|
const onEditbox = e.target instanceof HTMLDivElement && e.target.contentEditable;
|
||||||
|
|
@ -341,7 +341,7 @@ export default defineComponent({
|
||||||
triggerTextCommit() {
|
triggerTextCommit() {
|
||||||
if (!this.textInput) return;
|
if (!this.textInput) return;
|
||||||
const textCleaned = textInputCleanup(this.textInput.innerText);
|
const textCleaned = textInputCleanup(this.textInput.innerText);
|
||||||
this.editor.instance.on_change_text(textCleaned);
|
this.editor.instance.onChangeText(textCleaned);
|
||||||
},
|
},
|
||||||
displayEditableTextbox(displayEditableTextbox: DisplayEditableTextbox) {
|
displayEditableTextbox(displayEditableTextbox: DisplayEditableTextbox) {
|
||||||
this.textInput = document.createElement("DIV") as HTMLDivElement;
|
this.textInput = document.createElement("DIV") as HTMLDivElement;
|
||||||
|
|
@ -350,14 +350,14 @@ export default defineComponent({
|
||||||
else this.textInput.textContent = `${displayEditableTextbox.text}\n`;
|
else this.textInput.textContent = `${displayEditableTextbox.text}\n`;
|
||||||
|
|
||||||
this.textInput.contentEditable = "true";
|
this.textInput.contentEditable = "true";
|
||||||
this.textInput.style.width = displayEditableTextbox.line_width ? `${displayEditableTextbox.line_width}px` : "max-content";
|
this.textInput.style.width = displayEditableTextbox.lineWidth ? `${displayEditableTextbox.lineWidth}px` : "max-content";
|
||||||
this.textInput.style.height = "auto";
|
this.textInput.style.height = "auto";
|
||||||
this.textInput.style.fontSize = `${displayEditableTextbox.font_size}px`;
|
this.textInput.style.fontSize = `${displayEditableTextbox.fontSize}px`;
|
||||||
this.textInput.style.color = displayEditableTextbox.color.toRgbaCSS();
|
this.textInput.style.color = displayEditableTextbox.color.toRgbaCSS();
|
||||||
|
|
||||||
this.textInput.oninput = (): void => {
|
this.textInput.oninput = (): void => {
|
||||||
if (!this.textInput) return;
|
if (!this.textInput) return;
|
||||||
this.editor.instance.update_bounds(textInputCleanup(this.textInput.innerText));
|
this.editor.instance.updateBounds(textInputCleanup(this.textInput.innerText));
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
displayRemoveEditableTextbox() {
|
displayRemoveEditableTextbox() {
|
||||||
|
|
|
||||||
|
|
@ -23,16 +23,16 @@
|
||||||
<div class="indent" :style="{ marginLeft: layerIndent(listing.entry) }"></div>
|
<div class="indent" :style="{ marginLeft: layerIndent(listing.entry) }"></div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
v-if="listing.entry.layer_type === 'Folder'"
|
v-if="listing.entry.layerType === 'Folder'"
|
||||||
class="expand-arrow"
|
class="expand-arrow"
|
||||||
:class="{ expanded: listing.entry.layer_metadata.expanded }"
|
:class="{ expanded: listing.entry.layerMetadata.expanded }"
|
||||||
@click.stop="handleExpandArrowClick(listing.entry.path)"
|
@click.stop="handleExpandArrowClick(listing.entry.path)"
|
||||||
></button>
|
></button>
|
||||||
<LayoutRow
|
<LayoutRow
|
||||||
class="layer"
|
class="layer"
|
||||||
:class="{ selected: listing.entry.layer_metadata.selected }"
|
:class="{ selected: listing.entry.layerMetadata.selected }"
|
||||||
:data-index="index"
|
:data-index="index"
|
||||||
:title="`${listing.entry.name}${devMode ? '\nLayer Path: ' + listing.entry.path.join(' / ') : ''}` || null"
|
:title="listing.entry.tooltip"
|
||||||
:draggable="draggable"
|
:draggable="draggable"
|
||||||
@dragstart="(e) => draggable && dragStart(e, listing)"
|
@dragstart="(e) => draggable && dragStart(e, listing)"
|
||||||
@click.exact="(e) => selectLayer(false, false, false, listing, e)"
|
@click.exact="(e) => selectLayer(false, false, false, listing, e)"
|
||||||
|
|
@ -45,17 +45,17 @@
|
||||||
@click.alt="(e) => e.stopPropagation()"
|
@click.alt="(e) => e.stopPropagation()"
|
||||||
>
|
>
|
||||||
<LayoutRow class="layer-type-icon">
|
<LayoutRow class="layer-type-icon">
|
||||||
<IconLabel v-if="listing.entry.layer_type === 'Folder'" :icon="'NodeFolder'" :iconStyle="'Node'" title="Folder" />
|
<IconLabel v-if="listing.entry.layerType === 'Folder'" :icon="'NodeFolder'" :iconStyle="'Node'" title="Folder" />
|
||||||
<IconLabel v-else-if="listing.entry.layer_type === 'Image'" :icon="'NodeImage'" :iconStyle="'Node'" title="Image" />
|
<IconLabel v-else-if="listing.entry.layerType === 'Image'" :icon="'NodeImage'" :iconStyle="'Node'" title="Image" />
|
||||||
<IconLabel v-else-if="listing.entry.layer_type === 'Shape'" :icon="'NodeShape'" :iconStyle="'Node'" title="Shape" />
|
<IconLabel v-else-if="listing.entry.layerType === 'Shape'" :icon="'NodeShape'" :iconStyle="'Node'" title="Shape" />
|
||||||
<IconLabel v-else-if="listing.entry.layer_type === 'Text'" :icon="'NodeText'" :iconStyle="'Node'" title="Path" />
|
<IconLabel v-else-if="listing.entry.layerType === 'Text'" :icon="'NodeText'" :iconStyle="'Node'" title="Path" />
|
||||||
</LayoutRow>
|
</LayoutRow>
|
||||||
<LayoutRow class="layer-name" @dblclick="() => onEditLayerName(listing)">
|
<LayoutRow class="layer-name" @dblclick="() => onEditLayerName(listing)">
|
||||||
<input
|
<input
|
||||||
data-text-input
|
data-text-input
|
||||||
type="text"
|
type="text"
|
||||||
:value="listing.entry.name"
|
:value="listing.entry.name"
|
||||||
:placeholder="listing.entry.layer_type"
|
:placeholder="listing.entry.layerType"
|
||||||
:disabled="!listing.editingName"
|
:disabled="!listing.editingName"
|
||||||
@blur="() => onEditLayerNameDeselect(listing)"
|
@blur="() => onEditLayerNameDeselect(listing)"
|
||||||
@keydown.esc="onEditLayerNameDeselect(listing)"
|
@keydown.esc="onEditLayerNameDeselect(listing)"
|
||||||
|
|
@ -292,7 +292,6 @@ export default defineComponent({
|
||||||
// Layer data
|
// Layer data
|
||||||
layerCache: new Map() as Map<string, LayerPanelEntry>, // TODO: replace with BigUint64Array as index
|
layerCache: new Map() as Map<string, LayerPanelEntry>, // TODO: replace with BigUint64Array as index
|
||||||
layers: [] as LayerListingInfo[],
|
layers: [] as LayerListingInfo[],
|
||||||
devMode: process.env.NODE_ENV === "development",
|
|
||||||
|
|
||||||
// Interactive dragging
|
// Interactive dragging
|
||||||
draggable: true,
|
draggable: true,
|
||||||
|
|
@ -313,10 +312,10 @@ export default defineComponent({
|
||||||
return `${height}px`;
|
return `${height}px`;
|
||||||
},
|
},
|
||||||
toggleLayerVisibility(path: BigUint64Array) {
|
toggleLayerVisibility(path: BigUint64Array) {
|
||||||
this.editor.instance.toggle_layer_visibility(path);
|
this.editor.instance.toggleLayerVisibility(path);
|
||||||
},
|
},
|
||||||
handleExpandArrowClick(path: BigUint64Array) {
|
handleExpandArrowClick(path: BigUint64Array) {
|
||||||
this.editor.instance.toggle_layer_expansion(path);
|
this.editor.instance.toggleLayerExpansion(path);
|
||||||
},
|
},
|
||||||
async onEditLayerName(listing: LayerListingInfo) {
|
async onEditLayerName(listing: LayerListingInfo) {
|
||||||
if (listing.editingName) return;
|
if (listing.editingName) return;
|
||||||
|
|
@ -337,7 +336,7 @@ export default defineComponent({
|
||||||
|
|
||||||
const name = (inputElement as HTMLInputElement).value;
|
const name = (inputElement as HTMLInputElement).value;
|
||||||
listing.editingName = false;
|
listing.editingName = false;
|
||||||
this.editor.instance.set_layer_name(listing.entry.path, name);
|
this.editor.instance.setLayerName(listing.entry.path, name);
|
||||||
},
|
},
|
||||||
async onEditLayerNameDeselect(listing: LayerListingInfo) {
|
async onEditLayerNameDeselect(listing: LayerListingInfo) {
|
||||||
this.draggable = true;
|
this.draggable = true;
|
||||||
|
|
@ -354,14 +353,14 @@ export default defineComponent({
|
||||||
// Pressing the Ctrl key on a Mac, or the Cmd key on another platform, is a violation of the `.exact` qualifier so we filter it out here
|
// Pressing the Ctrl key on a Mac, or the Cmd key on another platform, is a violation of the `.exact` qualifier so we filter it out here
|
||||||
const opposite = platformIsMac() ? ctrl : cmd;
|
const opposite = platformIsMac() ? ctrl : cmd;
|
||||||
|
|
||||||
if (!opposite) this.editor.instance.select_layer(listing.entry.path, ctrlOrCmd, shift);
|
if (!opposite) this.editor.instance.selectLayer(listing.entry.path, ctrlOrCmd, shift);
|
||||||
|
|
||||||
// We always want to stop propagation so the click event doesn't pass through the layer and cause a deselection by clicking the layer panel background
|
// We always want to stop propagation so the click event doesn't pass through the layer and cause a deselection by clicking the layer panel background
|
||||||
// This is also why we cover the remaining cases not considered by the `.exact` qualifier, in the last two bindings on the layer element, with a `stopPropagation()` call
|
// This is also why we cover the remaining cases not considered by the `.exact` qualifier, in the last two bindings on the layer element, with a `stopPropagation()` call
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
},
|
},
|
||||||
async deselectAllLayers() {
|
async deselectAllLayers() {
|
||||||
this.editor.instance.deselect_all_layers();
|
this.editor.instance.deselectAllLayers();
|
||||||
},
|
},
|
||||||
calculateDragIndex(tree: HTMLElement, clientY: number): DraggingData {
|
calculateDragIndex(tree: HTMLElement, clientY: number): DraggingData {
|
||||||
const treeChildren = tree.children;
|
const treeChildren = tree.children;
|
||||||
|
|
@ -405,9 +404,9 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
// Inserting below current row
|
// Inserting below current row
|
||||||
else if (distance > -closest && distance > -RANGE_TO_INSERT_WITHIN_BOTTOM_FOLDER_NOT_ROOT && distance < 0) {
|
else if (distance > -closest && distance > -RANGE_TO_INSERT_WITHIN_BOTTOM_FOLDER_NOT_ROOT && distance < 0) {
|
||||||
insertFolder = layer.layer_type === "Folder" ? layer.path : layer.path.slice(0, layer.path.length - 1);
|
insertFolder = layer.layerType === "Folder" ? layer.path : layer.path.slice(0, layer.path.length - 1);
|
||||||
insertIndex = layer.layer_type === "Folder" ? 0 : folderIndex + 1;
|
insertIndex = layer.layerType === "Folder" ? 0 : folderIndex + 1;
|
||||||
highlightFolder = layer.layer_type === "Folder";
|
highlightFolder = layer.layerType === "Folder";
|
||||||
closest = -distance;
|
closest = -distance;
|
||||||
markerHeight = index === treeChildren.length - 1 ? rect.bottom - INSERT_MARK_OFFSET : rect.bottom;
|
markerHeight = index === treeChildren.length - 1 ? rect.bottom - INSERT_MARK_OFFSET : rect.bottom;
|
||||||
}
|
}
|
||||||
|
|
@ -426,7 +425,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
async dragStart(event: DragEvent, listing: LayerListingInfo) {
|
async dragStart(event: DragEvent, listing: LayerListingInfo) {
|
||||||
const layer = listing.entry;
|
const layer = listing.entry;
|
||||||
if (!layer.layer_metadata.selected) this.selectLayer(event.ctrlKey, event.metaKey, event.shiftKey, listing, event);
|
if (!layer.layerMetadata.selected) this.selectLayer(event.ctrlKey, event.metaKey, event.shiftKey, listing, event);
|
||||||
|
|
||||||
// Set style of cursor for drag
|
// Set style of cursor for drag
|
||||||
if (event.dataTransfer) {
|
if (event.dataTransfer) {
|
||||||
|
|
@ -448,7 +447,7 @@ export default defineComponent({
|
||||||
if (this.draggingData) {
|
if (this.draggingData) {
|
||||||
const { insertFolder, insertIndex } = this.draggingData;
|
const { insertFolder, insertIndex } = this.draggingData;
|
||||||
|
|
||||||
this.editor.instance.move_layer_in_tree(insertFolder, insertIndex);
|
this.editor.instance.moveLayerInTree(insertFolder, insertIndex);
|
||||||
|
|
||||||
this.draggingData = undefined;
|
this.draggingData = undefined;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="widget-layout">
|
<div class="widget-layout">
|
||||||
<component :is="LayoutGroupType(layoutRow)" :widgetData="layoutRow" :layoutTarget="layout.layout_target" v-for="(layoutRow, index) in layout.layout" :key="index" />
|
<component :is="LayoutGroupType(layoutRow)" :widgetData="layoutRow" :layoutTarget="layout.layoutTarget" v-for="(layoutRow, index) in layout.layout" :key="index" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateLayout(widgetId: bigint, value: unknown) {
|
updateLayout(widgetId: bigint, value: unknown) {
|
||||||
this.editor.instance.update_layout(this.layoutTarget, widgetId, value);
|
this.editor.instance.updateLayout(this.layoutTarget, widgetId, value);
|
||||||
},
|
},
|
||||||
withoutValue(props: Record<string, unknown>): Record<string, unknown> {
|
withoutValue(props: Record<string, unknown>): Record<string, unknown> {
|
||||||
const { value: _, ...rest } = props;
|
const { value: _, ...rest } = props;
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ const WidgetSection = defineComponent({
|
||||||
}),
|
}),
|
||||||
methods: {
|
methods: {
|
||||||
updateLayout(widgetId: bigint, value: unknown) {
|
updateLayout(widgetId: bigint, value: unknown) {
|
||||||
this.editor.instance.update_layout(this.layoutTarget, widgetId, value);
|
this.editor.instance.updateLayout(this.layoutTarget, widgetId, value);
|
||||||
},
|
},
|
||||||
layoutGroupType(layoutGroup: LayoutGroup): unknown {
|
layoutGroupType(layoutGroup: LayoutGroup): unknown {
|
||||||
if (isWidgetRow(layoutGroup)) return WidgetRow;
|
if (isWidgetRow(layoutGroup)) return WidgetRow;
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
:direction="'Bottom'"
|
:direction="'Bottom'"
|
||||||
:minWidth="240"
|
:minWidth="240"
|
||||||
:drawIcon="true"
|
:drawIcon="true"
|
||||||
:defaultAction="() => editor.instance.request_coming_soon_dialog()"
|
:defaultAction="() => editor.instance.requestComingSoonDialog()"
|
||||||
:ref="(ref: typeof MenuList) => ref && (entry.ref = ref)"
|
:ref="(ref: typeof MenuList) => ref && (entry.ref = ref)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -111,7 +111,7 @@ export default defineComponent({
|
||||||
group.map((entry) => ({
|
group.map((entry) => ({
|
||||||
...entry,
|
...entry,
|
||||||
children: entry.children ? menuEntryToFrontendMenuEntry(entry.children) : undefined,
|
children: entry.children ? menuEntryToFrontendMenuEntry(entry.children) : undefined,
|
||||||
action: (): void => this.editor.instance.update_layout(updateMenuBarLayout.layout_target, entry.action.widgetId, undefined),
|
action: (): void => this.editor.instance.updateLayout(updateMenuBarLayout.layoutTarget, entry.action.widgetId, undefined),
|
||||||
shortcutRequiresLock: entry.shortcut ? shortcutRequiresLock(entry.shortcut.keys) : undefined,
|
shortcutRequiresLock: entry.shortcut ? shortcutRequiresLock(entry.shortcut.keys) : undefined,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -105,11 +105,11 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
primaryColorChanged(color: RGBA) {
|
primaryColorChanged(color: RGBA) {
|
||||||
const newColor = rgbaToDecimalRgba(color);
|
const newColor = rgbaToDecimalRgba(color);
|
||||||
this.editor.instance.update_primary_color(newColor.r, newColor.g, newColor.b, newColor.a);
|
this.editor.instance.updatePrimaryColor(newColor.r, newColor.g, newColor.b, newColor.a);
|
||||||
},
|
},
|
||||||
secondaryColorChanged(color: RGBA) {
|
secondaryColorChanged(color: RGBA) {
|
||||||
const newColor = rgbaToDecimalRgba(color);
|
const newColor = rgbaToDecimalRgba(color);
|
||||||
this.editor.instance.update_secondary_color(newColor.r, newColor.g, newColor.b, newColor.a);
|
this.editor.instance.updateSecondaryColor(newColor.r, newColor.g, newColor.b, newColor.a);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.editor.subscriptions.subscribeJsMessage(UpdateInputHints, (updateInputHints) => {
|
this.editor.subscriptions.subscribeJsMessage(UpdateInputHints, (updateInputHints) => {
|
||||||
this.hintData = updateInputHints.hint_data;
|
this.hintData = updateInputHints.hintData;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -252,10 +252,10 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
newDocument() {
|
newDocument() {
|
||||||
this.editor.instance.new_document_dialog();
|
this.editor.instance.newDocumentDialog();
|
||||||
},
|
},
|
||||||
openDocument() {
|
openDocument() {
|
||||||
this.editor.instance.document_open();
|
this.editor.instance.documentOpen();
|
||||||
},
|
},
|
||||||
platformModifiers(reservedKey: boolean): KeysGroup {
|
platformModifiers(reservedKey: boolean): KeysGroup {
|
||||||
// TODO: Remove this by properly feeding these keys from a layout provided by the backend
|
// TODO: Remove this by properly feeding these keys from a layout provided by the backend
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@
|
||||||
:tabCloseButtons="true"
|
:tabCloseButtons="true"
|
||||||
:tabMinWidths="true"
|
:tabMinWidths="true"
|
||||||
:tabLabels="portfolio.state.documents.map((doc) => doc.displayName)"
|
:tabLabels="portfolio.state.documents.map((doc) => doc.displayName)"
|
||||||
:clickAction="(tabIndex) => editor.instance.select_document(portfolio.state.documents[tabIndex].id)"
|
:clickAction="(tabIndex) => editor.instance.selectDocument(portfolio.state.documents[tabIndex].id)"
|
||||||
:closeAction="(tabIndex) => editor.instance.close_document_with_confirmation(portfolio.state.documents[tabIndex].id)"
|
:closeAction="(tabIndex) => editor.instance.closeDocumentWithConfirmation(portfolio.state.documents[tabIndex].id)"
|
||||||
:tabActiveIndex="portfolio.state.activeDocumentIndex"
|
:tabActiveIndex="portfolio.state.activeDocumentIndex"
|
||||||
ref="documentsPanel"
|
ref="documentsPanel"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,15 @@ import { UpdateImageData } from "@/wasm-communication/messages";
|
||||||
export function createBlobManager(editor: Editor): void {
|
export function createBlobManager(editor: Editor): void {
|
||||||
// Subscribe to process backend event
|
// Subscribe to process backend event
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateImageData, (updateImageData) => {
|
editor.subscriptions.subscribeJsMessage(UpdateImageData, (updateImageData) => {
|
||||||
updateImageData.image_data.forEach(async (element) => {
|
updateImageData.imageData.forEach(async (element) => {
|
||||||
// Using updateImageData.image_data.buffer returns undefined for some reason?
|
// Using updateImageData.imageData.buffer returns undefined for some reason?
|
||||||
const blob = new Blob([new Uint8Array(element.image_data.values()).buffer], { type: element.mime });
|
const blob = new Blob([new Uint8Array(element.imageData.values()).buffer], { type: element.mime });
|
||||||
|
|
||||||
const url = URL.createObjectURL(blob);
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
const image = await createImageBitmap(blob);
|
const image = await createImageBitmap(blob);
|
||||||
|
|
||||||
editor.instance.set_image_blob_url(element.path, url, image.width, image.height);
|
editor.instance.setImageBlobUrl(element.path, url, image.width, image.height);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,6 @@ export function createClipboardManager(editor: Editor): void {
|
||||||
// Subscribe to process backend event
|
// Subscribe to process backend event
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerTextCopy, (triggerTextCopy) => {
|
editor.subscriptions.subscribeJsMessage(TriggerTextCopy, (triggerTextCopy) => {
|
||||||
// If the Clipboard API is supported in the browser, copy text to the clipboard
|
// If the Clipboard API is supported in the browser, copy text to the clipboard
|
||||||
navigator.clipboard?.writeText?.(triggerTextCopy.copy_text);
|
navigator.clipboard?.writeText?.(triggerTextCopy.copyText);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
if (await shouldRedirectKeyboardEventToBackend(e)) {
|
if (await shouldRedirectKeyboardEventToBackend(e)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const modifiers = makeKeyboardModifiersBitfield(e);
|
const modifiers = makeKeyboardModifiersBitfield(e);
|
||||||
editor.instance.on_key_down(key, modifiers);
|
editor.instance.onKeyDown(key, modifiers);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,7 +121,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
if (await shouldRedirectKeyboardEventToBackend(e)) {
|
if (await shouldRedirectKeyboardEventToBackend(e)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const modifiers = makeKeyboardModifiersBitfield(e);
|
const modifiers = makeKeyboardModifiersBitfield(e);
|
||||||
editor.instance.on_key_up(key, modifiers);
|
editor.instance.onKeyUp(key, modifiers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,7 +146,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
}
|
}
|
||||||
|
|
||||||
const modifiers = makeKeyboardModifiersBitfield(e);
|
const modifiers = makeKeyboardModifiersBitfield(e);
|
||||||
editor.instance.on_mouse_move(e.clientX, e.clientY, e.buttons, modifiers);
|
editor.instance.onMouseMove(e.clientX, e.clientY, e.buttons, modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPointerDown(e: PointerEvent): void {
|
function onPointerDown(e: PointerEvent): void {
|
||||||
|
|
@ -162,13 +162,13 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inTextInput) {
|
if (!inTextInput) {
|
||||||
if (textInput) editor.instance.on_change_text(textInputCleanup(textInput.innerText));
|
if (textInput) editor.instance.onChangeText(textInputCleanup(textInput.innerText));
|
||||||
else viewportPointerInteractionOngoing = isTargetingCanvas instanceof Element;
|
else viewportPointerInteractionOngoing = isTargetingCanvas instanceof Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewportPointerInteractionOngoing) {
|
if (viewportPointerInteractionOngoing) {
|
||||||
const modifiers = makeKeyboardModifiersBitfield(e);
|
const modifiers = makeKeyboardModifiersBitfield(e);
|
||||||
editor.instance.on_mouse_down(e.clientX, e.clientY, e.buttons, modifiers);
|
editor.instance.onMouseDown(e.clientX, e.clientY, e.buttons, modifiers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,7 +177,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
|
|
||||||
if (!textInput) {
|
if (!textInput) {
|
||||||
const modifiers = makeKeyboardModifiersBitfield(e);
|
const modifiers = makeKeyboardModifiersBitfield(e);
|
||||||
editor.instance.on_mouse_up(e.clientX, e.clientY, e.buttons, modifiers);
|
editor.instance.onMouseUp(e.clientX, e.clientY, e.buttons, modifiers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,7 +186,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
|
|
||||||
if (!textInput) {
|
if (!textInput) {
|
||||||
const modifiers = makeKeyboardModifiersBitfield(e);
|
const modifiers = makeKeyboardModifiersBitfield(e);
|
||||||
editor.instance.on_double_click(e.clientX, e.clientY, e.buttons, modifiers);
|
editor.instance.onDoubleClick(e.clientX, e.clientY, e.buttons, modifiers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,7 +213,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
if (isTargetingCanvas) {
|
if (isTargetingCanvas) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const modifiers = makeKeyboardModifiersBitfield(e);
|
const modifiers = makeKeyboardModifiersBitfield(e);
|
||||||
editor.instance.on_wheel_scroll(e.clientX, e.clientY, e.buttons, e.deltaX, e.deltaY, e.deltaZ, modifiers);
|
editor.instance.onWheelScroll(e.clientX, e.clientY, e.buttons, e.deltaX, e.deltaY, e.deltaZ, modifiers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,20 +233,20 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
const flattened = boundsOfViewports.flat();
|
const flattened = boundsOfViewports.flat();
|
||||||
const data = Float64Array.from(flattened);
|
const data = Float64Array.from(flattened);
|
||||||
|
|
||||||
if (boundsOfViewports.length > 0) editor.instance.bounds_of_viewports(data);
|
if (boundsOfViewports.length > 0) editor.instance.boundsOfViewports(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onBeforeUnload(e: BeforeUnloadEvent): void {
|
function onBeforeUnload(e: BeforeUnloadEvent): void {
|
||||||
const activeDocument = document.state.documents[document.state.activeDocumentIndex];
|
const activeDocument = document.state.documents[document.state.activeDocumentIndex];
|
||||||
if (!activeDocument.is_saved) editor.instance.trigger_auto_save(activeDocument.id);
|
if (!activeDocument.isSaved) editor.instance.triggerAutoSave(activeDocument.id);
|
||||||
|
|
||||||
// Skip the message if the editor crashed, since work is already lost
|
// Skip the message if the editor crashed, since work is already lost
|
||||||
if (editor.instance.has_crashed()) return;
|
if (editor.instance.hasCrashed()) return;
|
||||||
|
|
||||||
// Skip the message during development, since it's annoying when testing
|
// Skip the message during development, since it's annoying when testing
|
||||||
if (process.env.NODE_ENV === "development") return;
|
if (process.env.NODE_ENV === "development") return;
|
||||||
|
|
||||||
const allDocumentsSaved = document.state.documents.reduce((acc, doc) => acc && doc.is_saved, true);
|
const allDocumentsSaved = document.state.documents.reduce((acc, doc) => acc && doc.isSaved, true);
|
||||||
if (!allDocumentsSaved) {
|
if (!allDocumentsSaved) {
|
||||||
e.returnValue = "Unsaved work will be lost if the web browser tab is closed. Close anyway?";
|
e.returnValue = "Unsaved work will be lost if the web browser tab is closed. Close anyway?";
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -262,7 +262,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
if (item.type === "text/plain") {
|
if (item.type === "text/plain") {
|
||||||
item.getAsString((text) => {
|
item.getAsString((text) => {
|
||||||
if (text.startsWith("graphite/layer: ")) {
|
if (text.startsWith("graphite/layer: ")) {
|
||||||
editor.instance.paste_serialized_data(text.substring(16, text.length));
|
editor.instance.pasteSerializedData(text.substring(16, text.length));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +272,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
file.arrayBuffer().then((buffer): void => {
|
file.arrayBuffer().then((buffer): void => {
|
||||||
const u8Array = new Uint8Array(buffer);
|
const u8Array = new Uint8Array(buffer);
|
||||||
|
|
||||||
editor.instance.paste_image(file.type, u8Array);
|
editor.instance.pasteImage(file.type, u8Array);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -305,7 +305,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
const text = reader.result as string;
|
const text = reader.result as string;
|
||||||
|
|
||||||
if (text.startsWith("graphite/layer: ")) {
|
if (text.startsWith("graphite/layer: ")) {
|
||||||
editor.instance.paste_serialized_data(text.substring(16, text.length));
|
editor.instance.pasteSerializedData(text.substring(16, text.length));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
reader.readAsText(blob);
|
reader.readAsText(blob);
|
||||||
|
|
@ -319,7 +319,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
reader.onload = (): void => {
|
reader.onload = (): void => {
|
||||||
const u8Array = new Uint8Array(reader.result as ArrayBuffer);
|
const u8Array = new Uint8Array(reader.result as ArrayBuffer);
|
||||||
|
|
||||||
editor.instance.paste_image(imageType, u8Array);
|
editor.instance.pasteImage(imageType, u8Array);
|
||||||
};
|
};
|
||||||
reader.readAsArrayBuffer(blob);
|
reader.readAsArrayBuffer(blob);
|
||||||
}
|
}
|
||||||
|
|
@ -343,7 +343,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
||||||
};
|
};
|
||||||
const message = Object.entries(matchMessage).find(([key]) => String(err).includes(key))?.[1] || String(err);
|
const message = Object.entries(matchMessage).find(([key]) => String(err).includes(key))?.[1] || String(err);
|
||||||
|
|
||||||
editor.instance.error_dialog("Cannot access clipboard", message);
|
editor.instance.errorDialog("Cannot access clipboard", message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export function createLocalizationManager(editor: Editor): void {
|
||||||
|
|
||||||
// Subscribe to process backend event
|
// Subscribe to process backend event
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerAboutGraphiteLocalizedCommitDate, (triggerAboutGraphiteLocalizedCommitDate) => {
|
editor.subscriptions.subscribeJsMessage(TriggerAboutGraphiteLocalizedCommitDate, (triggerAboutGraphiteLocalizedCommitDate) => {
|
||||||
const localized = localizeTimestamp(triggerAboutGraphiteLocalizedCommitDate.commit_date);
|
const localized = localizeTimestamp(triggerAboutGraphiteLocalizedCommitDate.commitDate);
|
||||||
editor.instance.request_about_graphite_dialog_with_localized_commit_date(localized);
|
editor.instance.requestAboutGraphiteDialogWithLocalizedCommitDate(localized);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export function createPanicManager(editor: Editor, dialogState: DialogState): vo
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(Error as any).stackTraceLimit = Infinity;
|
(Error as any).stackTraceLimit = Infinity;
|
||||||
const stackTrace = new Error().stack || "";
|
const stackTrace = new Error().stack || "";
|
||||||
const panicDetails = `${displayDialogPanic.panic_info}${stackTrace ? `\n\n${stackTrace}` : ""}`;
|
const panicDetails = `${displayDialogPanic.panicInfo}${stackTrace ? `\n\n${stackTrace}` : ""}`;
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(panicDetails);
|
console.error(panicDetails);
|
||||||
|
|
@ -29,8 +29,7 @@ function preparePanicDialog(header: string, details: string, panicDetails: strin
|
||||||
{ rowWidgets: [new Widget({ kind: "TextLabel", value: header, bold: true, italic: false, tableAlign: false, multiline: false }, 0n)] },
|
{ rowWidgets: [new Widget({ kind: "TextLabel", value: header, bold: true, italic: false, tableAlign: false, multiline: false }, 0n)] },
|
||||||
{ rowWidgets: [new Widget({ kind: "TextLabel", value: details, bold: false, italic: false, tableAlign: false, multiline: true }, 1n)] },
|
{ rowWidgets: [new Widget({ kind: "TextLabel", value: details, bold: false, italic: false, tableAlign: false, multiline: true }, 1n)] },
|
||||||
],
|
],
|
||||||
// eslint-disable-next-line camelcase
|
layoutTarget: null,
|
||||||
layout_target: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const reloadButton: TextButtonWidget = {
|
const reloadButton: TextButtonWidget = {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ export async function createPersistenceManager(editor: Editor, portfolio: Portfo
|
||||||
storeDocumentOrder();
|
storeDocumentOrder();
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerIndexedDbRemoveDocument, async (removeAutoSaveDocument) => {
|
editor.subscriptions.subscribeJsMessage(TriggerIndexedDbRemoveDocument, async (removeAutoSaveDocument) => {
|
||||||
removeDocument(removeAutoSaveDocument.document_id);
|
removeDocument(removeAutoSaveDocument.documentId);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Open the IndexedDB database connection and save it to this variable, which is a promise that resolves once the connection is open
|
// Open the IndexedDB database connection and save it to this variable, which is a promise that resolves once the connection is open
|
||||||
|
|
@ -61,7 +61,7 @@ export async function createPersistenceManager(editor: Editor, portfolio: Portfo
|
||||||
Error on opening IndexDB:
|
Error on opening IndexDB:
|
||||||
${dbOpenRequest.error}
|
${dbOpenRequest.error}
|
||||||
`;
|
`;
|
||||||
editor.instance.error_dialog("Document auto-save doesn't work in this browser", errorText);
|
editor.instance.errorDialog("Document auto-save doesn't work in this browser", errorText);
|
||||||
};
|
};
|
||||||
|
|
||||||
dbOpenRequest.onsuccess = (): void => {
|
dbOpenRequest.onsuccess = (): void => {
|
||||||
|
|
@ -82,10 +82,10 @@ export async function createPersistenceManager(editor: Editor, portfolio: Portfo
|
||||||
.map((id) => previouslySavedDocuments.find((autoSave) => autoSave.details.id === id))
|
.map((id) => previouslySavedDocuments.find((autoSave) => autoSave.details.id === id))
|
||||||
.filter((x) => x !== undefined) as TriggerIndexedDbWriteDocument[];
|
.filter((x) => x !== undefined) as TriggerIndexedDbWriteDocument[];
|
||||||
|
|
||||||
const currentDocumentVersion = editor.instance.graphite_document_version();
|
const currentDocumentVersion = editor.instance.graphiteDocumentVersion();
|
||||||
orderedSavedDocuments.forEach((doc: TriggerIndexedDbWriteDocument) => {
|
orderedSavedDocuments.forEach((doc: TriggerIndexedDbWriteDocument) => {
|
||||||
if (doc.version === currentDocumentVersion) {
|
if (doc.version === currentDocumentVersion) {
|
||||||
editor.instance.open_auto_saved_document(BigInt(doc.details.id), doc.details.name, doc.details.is_saved, doc.document);
|
editor.instance.openAutoSavedDocument(BigInt(doc.details.id), doc.details.name, doc.details.isSaved, doc.document);
|
||||||
} else {
|
} else {
|
||||||
removeDocument(doc.details.id);
|
removeDocument(doc.details.id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,12 +48,12 @@ export function createFontsState(editor: Editor) {
|
||||||
|
|
||||||
// Subscribe to process backend events
|
// Subscribe to process backend events
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerFontLoad, async (triggerFontLoad) => {
|
editor.subscriptions.subscribeJsMessage(TriggerFontLoad, async (triggerFontLoad) => {
|
||||||
const url = await getFontFileUrl(triggerFontLoad.font.font_family, triggerFontLoad.font.font_style);
|
const url = await getFontFileUrl(triggerFontLoad.font.fontFamily, triggerFontLoad.font.fontStyle);
|
||||||
if (url) {
|
if (url) {
|
||||||
const response = await (await fetch(url)).arrayBuffer();
|
const response = await (await fetch(url)).arrayBuffer();
|
||||||
editor.instance.on_font_load(triggerFontLoad.font.font_family, triggerFontLoad.font.font_style, url, new Uint8Array(response), triggerFontLoad.is_default);
|
editor.instance.onFontLoad(triggerFontLoad.font.fontFamily, triggerFontLoad.font.fontStyle, url, new Uint8Array(response), triggerFontLoad.isDefault);
|
||||||
} else {
|
} else {
|
||||||
editor.instance.error_dialog("Failed to load font", `The font ${triggerFontLoad.font.font_family} with style ${triggerFontLoad.font.font_style} does not exist`);
|
editor.instance.errorDialog("Failed to load font", `The font ${triggerFontLoad.font.fontFamily} with style ${triggerFontLoad.font.fontStyle} does not exist`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,21 @@ export function createPortfolioState(editor: Editor) {
|
||||||
|
|
||||||
// Set up message subscriptions on creation
|
// Set up message subscriptions on creation
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateOpenDocumentsList, (updateOpenDocumentList) => {
|
editor.subscriptions.subscribeJsMessage(UpdateOpenDocumentsList, (updateOpenDocumentList) => {
|
||||||
state.documents = updateOpenDocumentList.open_documents;
|
state.documents = updateOpenDocumentList.openDocuments;
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateActiveDocument, (updateActiveDocument) => {
|
editor.subscriptions.subscribeJsMessage(UpdateActiveDocument, (updateActiveDocument) => {
|
||||||
// Assume we receive a correct document id
|
// Assume we receive a correct document id
|
||||||
const activeId = state.documents.findIndex((doc) => doc.id === updateActiveDocument.document_id);
|
const activeId = state.documents.findIndex((doc) => doc.id === updateActiveDocument.documentId);
|
||||||
state.activeDocumentIndex = activeId;
|
state.activeDocumentIndex = activeId;
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerOpenDocument, async () => {
|
editor.subscriptions.subscribeJsMessage(TriggerOpenDocument, async () => {
|
||||||
const extension = editor.instance.file_save_suffix();
|
const extension = editor.instance.fileSaveSuffix();
|
||||||
const data = await upload(extension, "text");
|
const data = await upload(extension, "text");
|
||||||
editor.instance.open_document_file(data.filename, data.content);
|
editor.instance.openDocumentFile(data.filename, data.content);
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerImport, async () => {
|
editor.subscriptions.subscribeJsMessage(TriggerImport, async () => {
|
||||||
const data = await upload("image/*", "data");
|
const data = await upload("image/*", "data");
|
||||||
editor.instance.paste_image(data.type, Uint8Array.from(data.content));
|
editor.instance.pasteImage(data.type, Uint8Array.from(data.content));
|
||||||
});
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerFileDownload, (triggerFileDownload) => {
|
editor.subscriptions.subscribeJsMessage(TriggerFileDownload, (triggerFileDownload) => {
|
||||||
download(triggerFileDownload.name, triggerFileDownload.document);
|
download(triggerFileDownload.name, triggerFileDownload.document);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export async function initWasm(): Promise<void> {
|
||||||
|
|
||||||
// Provide a random starter seed which must occur after initializing the WASM module, since WASM can't generate its own random numbers
|
// Provide a random starter seed which must occur after initializing the WASM module, since WASM can't generate its own random numbers
|
||||||
const randomSeed = BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER));
|
const randomSeed = BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER));
|
||||||
wasmImport?.set_random_seed(randomSeed);
|
wasmImport?.setRandomSeed(randomSeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be called after running `initWasm()` and its promise resolving
|
// Should be called after running `initWasm()` and its promise resolving
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
/* eslint-disable camelcase */
|
|
||||||
/* eslint-disable max-classes-per-file */
|
/* eslint-disable max-classes-per-file */
|
||||||
|
|
||||||
import { Transform, Type, plainToClass } from "class-transformer";
|
import { Transform, Type, plainToClass } from "class-transformer";
|
||||||
|
|
@ -26,7 +25,7 @@ export class UpdateNodeGraphVisibility extends JsMessage {
|
||||||
|
|
||||||
export class UpdateOpenDocumentsList extends JsMessage {
|
export class UpdateOpenDocumentsList extends JsMessage {
|
||||||
@Type(() => FrontendDocumentDetails)
|
@Type(() => FrontendDocumentDetails)
|
||||||
readonly open_documents!: FrontendDocumentDetails[];
|
readonly openDocuments!: FrontendDocumentDetails[];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allows the auto save system to use a string for the id rather than a BigInt.
|
// Allows the auto save system to use a string for the id rather than a BigInt.
|
||||||
|
|
@ -36,12 +35,12 @@ export class UpdateOpenDocumentsList extends JsMessage {
|
||||||
export abstract class DocumentDetails {
|
export abstract class DocumentDetails {
|
||||||
readonly name!: string;
|
readonly name!: string;
|
||||||
|
|
||||||
readonly is_saved!: boolean;
|
readonly isSaved!: boolean;
|
||||||
|
|
||||||
readonly id!: bigint | string;
|
readonly id!: bigint | string;
|
||||||
|
|
||||||
get displayName(): string {
|
get displayName(): string {
|
||||||
return `${this.name}${this.is_saved ? "" : "*"}`;
|
return `${this.name}${this.isSaved ? "" : "*"}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,12 +65,12 @@ export class IndexedDbDocumentDetails extends DocumentDetails {
|
||||||
export class TriggerIndexedDbRemoveDocument extends JsMessage {
|
export class TriggerIndexedDbRemoveDocument extends JsMessage {
|
||||||
// Use a string since IndexedDB can not use BigInts for keys
|
// Use a string since IndexedDB can not use BigInts for keys
|
||||||
@Transform(({ value }: { value: bigint }) => value.toString())
|
@Transform(({ value }: { value: bigint }) => value.toString())
|
||||||
document_id!: string;
|
documentId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateInputHints extends JsMessage {
|
export class UpdateInputHints extends JsMessage {
|
||||||
@Type(() => HintInfo)
|
@Type(() => HintInfo)
|
||||||
readonly hint_data!: HintData;
|
readonly hintData!: HintData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HintData = HintGroup[];
|
export type HintData = HintGroup[];
|
||||||
|
|
@ -137,11 +136,11 @@ export class Color {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateActiveDocument extends JsMessage {
|
export class UpdateActiveDocument extends JsMessage {
|
||||||
readonly document_id!: bigint;
|
readonly documentId!: bigint;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DisplayDialogPanic extends JsMessage {
|
export class DisplayDialogPanic extends JsMessage {
|
||||||
readonly panic_info!: string;
|
readonly panicInfo!: string;
|
||||||
|
|
||||||
readonly header!: string;
|
readonly header!: string;
|
||||||
|
|
||||||
|
|
@ -245,11 +244,11 @@ interface DataBuffer {
|
||||||
length: bigint;
|
length: bigint;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newUpdateDocumentLayerTreeStructure(input: { data_buffer: DataBuffer }, wasm: WasmRawInstance): UpdateDocumentLayerTreeStructure {
|
export function newUpdateDocumentLayerTreeStructure(input: { dataBuffer: DataBuffer }, wasm: WasmRawInstance): UpdateDocumentLayerTreeStructure {
|
||||||
const pointerNum = Number(input.data_buffer.pointer);
|
const pointerNum = Number(input.dataBuffer.pointer);
|
||||||
const lengthNum = Number(input.data_buffer.length);
|
const lengthNum = Number(input.dataBuffer.length);
|
||||||
|
|
||||||
const wasmMemoryBuffer = wasm.wasm_memory().buffer;
|
const wasmMemoryBuffer = wasm.wasmMemory().buffer;
|
||||||
|
|
||||||
// Decode the folder structure encoding
|
// Decode the folder structure encoding
|
||||||
const encoding = new DataView(wasmMemoryBuffer, pointerNum, lengthNum);
|
const encoding = new DataView(wasmMemoryBuffer, pointerNum, lengthNum);
|
||||||
|
|
@ -302,9 +301,9 @@ export function newUpdateDocumentLayerTreeStructure(input: { data_buffer: DataBu
|
||||||
export class DisplayEditableTextbox extends JsMessage {
|
export class DisplayEditableTextbox extends JsMessage {
|
||||||
readonly text!: string;
|
readonly text!: string;
|
||||||
|
|
||||||
readonly line_width!: undefined | number;
|
readonly lineWidth!: undefined | number;
|
||||||
|
|
||||||
readonly font_size!: number;
|
readonly fontSize!: number;
|
||||||
|
|
||||||
@Type(() => Color)
|
@Type(() => Color)
|
||||||
readonly color!: Color;
|
readonly color!: Color;
|
||||||
|
|
@ -312,7 +311,7 @@ export class DisplayEditableTextbox extends JsMessage {
|
||||||
|
|
||||||
export class UpdateImageData extends JsMessage {
|
export class UpdateImageData extends JsMessage {
|
||||||
@Type(() => ImageData)
|
@Type(() => ImageData)
|
||||||
readonly image_data!: ImageData[];
|
readonly imageData!: ImageData[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DisplayRemoveEditableTextbox extends JsMessage {}
|
export class DisplayRemoveEditableTextbox extends JsMessage {}
|
||||||
|
|
@ -327,13 +326,13 @@ export class LayerPanelEntry {
|
||||||
|
|
||||||
visible!: boolean;
|
visible!: boolean;
|
||||||
|
|
||||||
layer_type!: LayerType;
|
layerType!: LayerType;
|
||||||
|
|
||||||
@Transform(({ value }: { value: bigint[] }) => new BigUint64Array(value))
|
@Transform(({ value }: { value: bigint[] }) => new BigUint64Array(value))
|
||||||
path!: BigUint64Array;
|
path!: BigUint64Array;
|
||||||
|
|
||||||
@Type(() => LayerMetadata)
|
@Type(() => LayerMetadata)
|
||||||
layer_metadata!: LayerMetadata;
|
layerMetadata!: LayerMetadata;
|
||||||
|
|
||||||
thumbnail!: string;
|
thumbnail!: string;
|
||||||
}
|
}
|
||||||
|
|
@ -351,22 +350,22 @@ export class ImageData {
|
||||||
|
|
||||||
readonly mime!: string;
|
readonly mime!: string;
|
||||||
|
|
||||||
readonly image_data!: Uint8Array;
|
readonly imageData!: Uint8Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DisplayDialogDismiss extends JsMessage {}
|
export class DisplayDialogDismiss extends JsMessage {}
|
||||||
|
|
||||||
export class Font {
|
export class Font {
|
||||||
font_family!: string;
|
fontFamily!: string;
|
||||||
|
|
||||||
font_style!: string;
|
fontStyle!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TriggerFontLoad extends JsMessage {
|
export class TriggerFontLoad extends JsMessage {
|
||||||
@Type(() => Font)
|
@Type(() => Font)
|
||||||
font!: Font;
|
font!: Font;
|
||||||
|
|
||||||
is_default!: boolean;
|
isDefault!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TriggerVisitLink extends JsMessage {
|
export class TriggerVisitLink extends JsMessage {
|
||||||
|
|
@ -376,11 +375,11 @@ export class TriggerVisitLink extends JsMessage {
|
||||||
export class TriggerTextCommit extends JsMessage {}
|
export class TriggerTextCommit extends JsMessage {}
|
||||||
|
|
||||||
export class TriggerTextCopy extends JsMessage {
|
export class TriggerTextCopy extends JsMessage {
|
||||||
readonly copy_text!: string;
|
readonly copyText!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TriggerAboutGraphiteLocalizedCommitDate extends JsMessage {
|
export class TriggerAboutGraphiteLocalizedCommitDate extends JsMessage {
|
||||||
readonly commit_date!: string;
|
readonly commitDate!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TriggerViewportResize extends JsMessage {}
|
export class TriggerViewportResize extends JsMessage {}
|
||||||
|
|
@ -647,13 +646,13 @@ function hoistWidgetHolders(widgetHolders: any[]): Widget[] {
|
||||||
// WIDGET LAYOUT
|
// WIDGET LAYOUT
|
||||||
|
|
||||||
export interface WidgetLayout {
|
export interface WidgetLayout {
|
||||||
layout_target: unknown;
|
layoutTarget: unknown;
|
||||||
layout: LayoutGroup[];
|
layout: LayoutGroup[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function defaultWidgetLayout(): WidgetLayout {
|
export function defaultWidgetLayout(): WidgetLayout {
|
||||||
return {
|
return {
|
||||||
layout_target: null,
|
layoutTarget: null,
|
||||||
layout: [],
|
layout: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -708,7 +707,7 @@ function createWidgetLayout(widgetLayout: any[]): LayoutGroup[] {
|
||||||
// WIDGET LAYOUTS
|
// WIDGET LAYOUTS
|
||||||
|
|
||||||
export class UpdateDialogDetails extends JsMessage implements WidgetLayout {
|
export class UpdateDialogDetails extends JsMessage implements WidgetLayout {
|
||||||
layout_target!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|
@ -717,7 +716,7 @@ export class UpdateDialogDetails extends JsMessage implements WidgetLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateDocumentModeLayout extends JsMessage implements WidgetLayout {
|
export class UpdateDocumentModeLayout extends JsMessage implements WidgetLayout {
|
||||||
layout_target!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|
@ -726,7 +725,7 @@ export class UpdateDocumentModeLayout extends JsMessage implements WidgetLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateToolOptionsLayout extends JsMessage implements WidgetLayout {
|
export class UpdateToolOptionsLayout extends JsMessage implements WidgetLayout {
|
||||||
layout_target!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|
@ -735,7 +734,7 @@ export class UpdateToolOptionsLayout extends JsMessage implements WidgetLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateDocumentBarLayout extends JsMessage implements WidgetLayout {
|
export class UpdateDocumentBarLayout extends JsMessage implements WidgetLayout {
|
||||||
layout_target!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|
@ -744,7 +743,7 @@ export class UpdateDocumentBarLayout extends JsMessage implements WidgetLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateToolShelfLayout extends JsMessage implements WidgetLayout {
|
export class UpdateToolShelfLayout extends JsMessage implements WidgetLayout {
|
||||||
layout_target!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|
@ -753,7 +752,7 @@ export class UpdateToolShelfLayout extends JsMessage implements WidgetLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateWorkingColorsLayout extends JsMessage implements WidgetLayout {
|
export class UpdateWorkingColorsLayout extends JsMessage implements WidgetLayout {
|
||||||
layout_target!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|
@ -762,7 +761,7 @@ export class UpdateWorkingColorsLayout extends JsMessage implements WidgetLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdatePropertyPanelOptionsLayout extends JsMessage implements WidgetLayout {
|
export class UpdatePropertyPanelOptionsLayout extends JsMessage implements WidgetLayout {
|
||||||
layout_target!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|
@ -771,7 +770,7 @@ export class UpdatePropertyPanelOptionsLayout extends JsMessage implements Widge
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdatePropertyPanelSectionsLayout extends JsMessage implements WidgetLayout {
|
export class UpdatePropertyPanelSectionsLayout extends JsMessage implements WidgetLayout {
|
||||||
layout_target!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|
@ -780,7 +779,7 @@ export class UpdatePropertyPanelSectionsLayout extends JsMessage implements Widg
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateLayerTreeOptionsLayout extends JsMessage implements WidgetLayout {
|
export class UpdateLayerTreeOptionsLayout extends JsMessage implements WidgetLayout {
|
||||||
layout_target!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|
@ -789,7 +788,7 @@ export class UpdateLayerTreeOptionsLayout extends JsMessage implements WidgetLay
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateMenuBarLayout extends JsMessage {
|
export class UpdateMenuBarLayout extends JsMessage {
|
||||||
layout_target!: unknown;
|
layoutTarget!: unknown;
|
||||||
|
|
||||||
// TODO: Replace `any` with correct typing
|
// TODO: Replace `any` with correct typing
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ module.exports = {
|
||||||
chainWebpack: (config) => {
|
chainWebpack: (config) => {
|
||||||
// WASM Pack Plugin integrates compiled Rust code (.wasm) and generated wasm-bindgen code (.js) with the webpack bundle
|
// WASM Pack Plugin integrates compiled Rust code (.wasm) and generated wasm-bindgen code (.js) with the webpack bundle
|
||||||
// Use this JS to import the bundled Rust entry points: const wasm = import("@/../wasm/pkg").then(panicProxy);
|
// Use this JS to import the bundled Rust entry points: const wasm = import("@/../wasm/pkg").then(panicProxy);
|
||||||
// Then call WASM functions with: (await wasm).function_name()
|
// Then call WASM functions with: (await wasm).functionName()
|
||||||
// https://github.com/wasm-tool/wasm-pack-plugin
|
// https://github.com/wasm-tool/wasm-pack-plugin
|
||||||
config
|
config
|
||||||
// https://cli.vuejs.org/guide/webpack.html#modifying-options-of-a-plugin
|
// https://cli.vuejs.org/guide/webpack.html#modifying-options-of-a-plugin
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,13 @@ use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
/// Set the random seed used by the editor by calling this from JS upon initialization.
|
/// Set the random seed used by the editor by calling this from JS upon initialization.
|
||||||
/// This is necessary because WASM doesn't have a random number generator.
|
/// This is necessary because WASM doesn't have a random number generator.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen(js_name = setRandomSeed)]
|
||||||
pub fn set_random_seed(seed: u64) {
|
pub fn set_random_seed(seed: u64) {
|
||||||
editor::application::set_uuid_seed(seed);
|
editor::application::set_uuid_seed(seed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides a handle to access the raw WASM memory
|
/// Provides a handle to access the raw WASM memory
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen(js_name = wasmMemory)]
|
||||||
pub fn wasm_memory() -> JsValue {
|
pub fn wasm_memory() -> JsValue {
|
||||||
wasm_bindgen::memory()
|
wasm_bindgen::memory()
|
||||||
}
|
}
|
||||||
|
|
@ -111,6 +111,7 @@ impl JsEditorHandle {
|
||||||
// the backend from the web frontend.
|
// the backend from the web frontend.
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = initAfterFrontendReady)]
|
||||||
pub fn init_after_frontend_ready(&self, platform: String) {
|
pub fn init_after_frontend_ready(&self, platform: String) {
|
||||||
let platform = match platform.as_str() {
|
let platform = match platform.as_str() {
|
||||||
"Windows" => Platform::Windows,
|
"Windows" => Platform::Windows,
|
||||||
|
|
@ -124,29 +125,32 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Displays a dialog with an error message
|
/// Displays a dialog with an error message
|
||||||
|
#[wasm_bindgen(js_name = errorDialog)]
|
||||||
pub fn error_dialog(&self, title: String, description: String) {
|
pub fn error_dialog(&self, title: String, description: String) {
|
||||||
let message = DialogMessage::DisplayDialogError { title, description };
|
let message = DialogMessage::DisplayDialogError { title, description };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Answer whether or not the editor has crashed
|
/// Answer whether or not the editor has crashed
|
||||||
|
#[wasm_bindgen(js_name = hasCrashed)]
|
||||||
pub fn has_crashed(&self) -> bool {
|
pub fn has_crashed(&self) -> bool {
|
||||||
EDITOR_HAS_CRASHED.load(Ordering::SeqCst)
|
EDITOR_HAS_CRASHED.load(Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the constant `FILE_SAVE_SUFFIX`
|
/// Get the constant `FILE_SAVE_SUFFIX`
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen(js_name = fileSaveSuffix)]
|
||||||
pub fn file_save_suffix(&self) -> String {
|
pub fn file_save_suffix(&self) -> String {
|
||||||
FILE_SAVE_SUFFIX.into()
|
FILE_SAVE_SUFFIX.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the constant `GRAPHITE_DOCUMENT_VERSION`
|
/// Get the constant `GRAPHITE_DOCUMENT_VERSION`
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen(js_name = graphiteDocumentVersion)]
|
||||||
pub fn graphite_document_version(&self) -> String {
|
pub fn graphite_document_version(&self) -> String {
|
||||||
GRAPHITE_DOCUMENT_VERSION.to_string()
|
GRAPHITE_DOCUMENT_VERSION.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update layout of a given UI
|
/// Update layout of a given UI
|
||||||
|
#[wasm_bindgen(js_name = updateLayout)]
|
||||||
pub fn update_layout(&self, layout_target: JsValue, widget_id: u64, value: JsValue) -> Result<(), JsValue> {
|
pub fn update_layout(&self, layout_target: JsValue, widget_id: u64, value: JsValue) -> Result<(), JsValue> {
|
||||||
match (from_value(layout_target), from_value(value)) {
|
match (from_value(layout_target), from_value(value)) {
|
||||||
(Ok(layout_target), Ok(value)) => {
|
(Ok(layout_target), Ok(value)) => {
|
||||||
|
|
@ -158,21 +162,25 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = selectDocument)]
|
||||||
pub fn select_document(&self, document_id: u64) {
|
pub fn select_document(&self, document_id: u64) {
|
||||||
let message = PortfolioMessage::SelectDocument { document_id };
|
let message = PortfolioMessage::SelectDocument { document_id };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = newDocumentDialog)]
|
||||||
pub fn new_document_dialog(&self) {
|
pub fn new_document_dialog(&self) {
|
||||||
let message = DialogMessage::RequestNewDocumentDialog;
|
let message = DialogMessage::RequestNewDocumentDialog;
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = documentOpen)]
|
||||||
pub fn document_open(&self) {
|
pub fn document_open(&self) {
|
||||||
let message = PortfolioMessage::OpenDocument;
|
let message = PortfolioMessage::OpenDocument;
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = openDocumentFile)]
|
||||||
pub fn open_document_file(&self, document_name: String, document_serialized_content: String) {
|
pub fn open_document_file(&self, document_name: String, document_serialized_content: String) {
|
||||||
let message = PortfolioMessage::OpenDocumentFile {
|
let message = PortfolioMessage::OpenDocumentFile {
|
||||||
document_name,
|
document_name,
|
||||||
|
|
@ -181,6 +189,7 @@ impl JsEditorHandle {
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = openAutoSavedDocument)]
|
||||||
pub fn open_auto_saved_document(&self, document_id: u64, document_name: String, document_is_saved: bool, document_serialized_content: String) {
|
pub fn open_auto_saved_document(&self, document_id: u64, document_name: String, document_is_saved: bool, document_serialized_content: String) {
|
||||||
let message = PortfolioMessage::OpenDocumentFileWithId {
|
let message = PortfolioMessage::OpenDocumentFileWithId {
|
||||||
document_id,
|
document_id,
|
||||||
|
|
@ -191,21 +200,25 @@ impl JsEditorHandle {
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = triggerAutoSave)]
|
||||||
pub fn trigger_auto_save(&self, document_id: u64) {
|
pub fn trigger_auto_save(&self, document_id: u64) {
|
||||||
let message = PortfolioMessage::AutoSaveDocument { document_id };
|
let message = PortfolioMessage::AutoSaveDocument { document_id };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = closeDocumentWithConfirmation)]
|
||||||
pub fn close_document_with_confirmation(&self, document_id: u64) {
|
pub fn close_document_with_confirmation(&self, document_id: u64) {
|
||||||
let message = PortfolioMessage::CloseDocumentWithConfirmation { document_id };
|
let message = PortfolioMessage::CloseDocumentWithConfirmation { document_id };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = requestAboutGraphiteDialogWithLocalizedCommitDate)]
|
||||||
pub fn request_about_graphite_dialog_with_localized_commit_date(&self, localized_commit_date: String) {
|
pub fn request_about_graphite_dialog_with_localized_commit_date(&self, localized_commit_date: String) {
|
||||||
let message = DialogMessage::RequestAboutGraphiteDialogWithLocalizedCommitDate { localized_commit_date };
|
let message = DialogMessage::RequestAboutGraphiteDialogWithLocalizedCommitDate { localized_commit_date };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen(js_name = requestComingSoonDialog)]
|
||||||
pub fn request_coming_soon_dialog(&self, issue: Option<i32>) {
|
pub fn request_coming_soon_dialog(&self, issue: Option<i32>) {
|
||||||
let message = DialogMessage::RequestComingSoonDialog { issue };
|
let message = DialogMessage::RequestComingSoonDialog { issue };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
|
|
@ -213,6 +226,7 @@ impl JsEditorHandle {
|
||||||
|
|
||||||
/// Send new bounds when document panel viewports get resized or moved within the editor
|
/// Send new bounds when document panel viewports get resized or moved within the editor
|
||||||
/// [left, top, right, bottom]...
|
/// [left, top, right, bottom]...
|
||||||
|
#[wasm_bindgen(js_name = boundsOfViewports)]
|
||||||
pub fn bounds_of_viewports(&self, bounds_of_viewports: &[f64]) {
|
pub fn bounds_of_viewports(&self, bounds_of_viewports: &[f64]) {
|
||||||
let chunked: Vec<_> = bounds_of_viewports.chunks(4).map(ViewportBounds::from_slice).collect();
|
let chunked: Vec<_> = bounds_of_viewports.chunks(4).map(ViewportBounds::from_slice).collect();
|
||||||
|
|
||||||
|
|
@ -221,6 +235,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mouse movement within the screenspace bounds of the viewport
|
/// Mouse movement within the screenspace bounds of the viewport
|
||||||
|
#[wasm_bindgen(js_name = onMouseMove)]
|
||||||
pub fn on_mouse_move(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
|
pub fn on_mouse_move(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
|
||||||
let editor_mouse_state = EditorMouseState::from_keys_and_editor_position(mouse_keys, (x, y).into());
|
let editor_mouse_state = EditorMouseState::from_keys_and_editor_position(mouse_keys, (x, y).into());
|
||||||
|
|
||||||
|
|
@ -231,6 +246,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mouse scrolling within the screenspace bounds of the viewport
|
/// Mouse scrolling within the screenspace bounds of the viewport
|
||||||
|
#[wasm_bindgen(js_name = onWheelScroll)]
|
||||||
pub fn on_wheel_scroll(&self, x: f64, y: f64, mouse_keys: u8, wheel_delta_x: i32, wheel_delta_y: i32, wheel_delta_z: i32, modifiers: u8) {
|
pub fn on_wheel_scroll(&self, x: f64, y: f64, mouse_keys: u8, wheel_delta_x: i32, wheel_delta_y: i32, wheel_delta_z: i32, modifiers: u8) {
|
||||||
let mut editor_mouse_state = EditorMouseState::from_keys_and_editor_position(mouse_keys, (x, y).into());
|
let mut editor_mouse_state = EditorMouseState::from_keys_and_editor_position(mouse_keys, (x, y).into());
|
||||||
editor_mouse_state.scroll_delta = ScrollDelta::new(wheel_delta_x, wheel_delta_y, wheel_delta_z);
|
editor_mouse_state.scroll_delta = ScrollDelta::new(wheel_delta_x, wheel_delta_y, wheel_delta_z);
|
||||||
|
|
@ -242,6 +258,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mouse button depressed within screenspace the bounds of the viewport
|
/// A mouse button depressed within screenspace the bounds of the viewport
|
||||||
|
#[wasm_bindgen(js_name = onMouseDown)]
|
||||||
pub fn on_mouse_down(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
|
pub fn on_mouse_down(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
|
||||||
let editor_mouse_state = EditorMouseState::from_keys_and_editor_position(mouse_keys, (x, y).into());
|
let editor_mouse_state = EditorMouseState::from_keys_and_editor_position(mouse_keys, (x, y).into());
|
||||||
|
|
||||||
|
|
@ -252,6 +269,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mouse button released
|
/// A mouse button released
|
||||||
|
#[wasm_bindgen(js_name = onMouseUp)]
|
||||||
pub fn on_mouse_up(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
|
pub fn on_mouse_up(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
|
||||||
let editor_mouse_state = EditorMouseState::from_keys_and_editor_position(mouse_keys, (x, y).into());
|
let editor_mouse_state = EditorMouseState::from_keys_and_editor_position(mouse_keys, (x, y).into());
|
||||||
|
|
||||||
|
|
@ -262,6 +280,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mouse double clicked
|
/// Mouse double clicked
|
||||||
|
#[wasm_bindgen(js_name = onDoubleClick)]
|
||||||
pub fn on_double_click(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
|
pub fn on_double_click(&self, x: f64, y: f64, mouse_keys: u8, modifiers: u8) {
|
||||||
let editor_mouse_state = EditorMouseState::from_keys_and_editor_position(mouse_keys, (x, y).into());
|
let editor_mouse_state = EditorMouseState::from_keys_and_editor_position(mouse_keys, (x, y).into());
|
||||||
let modifier_keys = ModifierKeys::from_bits(modifiers).expect("Invalid modifier keys");
|
let modifier_keys = ModifierKeys::from_bits(modifiers).expect("Invalid modifier keys");
|
||||||
|
|
@ -271,6 +290,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A keyboard button depressed within screenspace the bounds of the viewport
|
/// A keyboard button depressed within screenspace the bounds of the viewport
|
||||||
|
#[wasm_bindgen(js_name = onKeyDown)]
|
||||||
pub fn on_key_down(&self, name: String, modifiers: u8) {
|
pub fn on_key_down(&self, name: String, modifiers: u8) {
|
||||||
let key = translate_key(&name);
|
let key = translate_key(&name);
|
||||||
let modifier_keys = ModifierKeys::from_bits(modifiers).expect("Invalid modifier keys");
|
let modifier_keys = ModifierKeys::from_bits(modifiers).expect("Invalid modifier keys");
|
||||||
|
|
@ -282,6 +302,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A keyboard button released
|
/// A keyboard button released
|
||||||
|
#[wasm_bindgen(js_name = onKeyUp)]
|
||||||
pub fn on_key_up(&self, name: String, modifiers: u8) {
|
pub fn on_key_up(&self, name: String, modifiers: u8) {
|
||||||
let key = translate_key(&name);
|
let key = translate_key(&name);
|
||||||
let modifier_keys = ModifierKeys::from_bits(modifiers).expect("Invalid modifier keys");
|
let modifier_keys = ModifierKeys::from_bits(modifiers).expect("Invalid modifier keys");
|
||||||
|
|
@ -293,6 +314,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A text box was committed
|
/// A text box was committed
|
||||||
|
#[wasm_bindgen(js_name = onChangeText)]
|
||||||
pub fn on_change_text(&self, new_text: String) -> Result<(), JsValue> {
|
pub fn on_change_text(&self, new_text: String) -> Result<(), JsValue> {
|
||||||
let message = TextToolMessage::TextChange { new_text };
|
let message = TextToolMessage::TextChange { new_text };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
|
|
@ -301,6 +323,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A font has been downloaded
|
/// A font has been downloaded
|
||||||
|
#[wasm_bindgen(js_name = onFontLoad)]
|
||||||
pub fn on_font_load(&self, font_family: String, font_style: String, preview_url: String, data: Vec<u8>, is_default: bool) -> Result<(), JsValue> {
|
pub fn on_font_load(&self, font_family: String, font_style: String, preview_url: String, data: Vec<u8>, is_default: bool) -> Result<(), JsValue> {
|
||||||
let message = PortfolioMessage::FontLoaded {
|
let message = PortfolioMessage::FontLoaded {
|
||||||
font_family,
|
font_family,
|
||||||
|
|
@ -315,6 +338,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A text box was changed
|
/// A text box was changed
|
||||||
|
#[wasm_bindgen(js_name = updateBounds)]
|
||||||
pub fn update_bounds(&self, new_text: String) -> Result<(), JsValue> {
|
pub fn update_bounds(&self, new_text: String) -> Result<(), JsValue> {
|
||||||
let message = TextToolMessage::UpdateBounds { new_text };
|
let message = TextToolMessage::UpdateBounds { new_text };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
|
|
@ -323,6 +347,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update primary color
|
/// Update primary color
|
||||||
|
#[wasm_bindgen(js_name = updatePrimaryColor)]
|
||||||
pub fn update_primary_color(&self, red: f32, green: f32, blue: f32, alpha: f32) -> Result<(), JsValue> {
|
pub fn update_primary_color(&self, red: f32, green: f32, blue: f32, alpha: f32) -> Result<(), JsValue> {
|
||||||
let primary_color = match Color::from_rgbaf32(red, green, blue, alpha) {
|
let primary_color = match Color::from_rgbaf32(red, green, blue, alpha) {
|
||||||
Some(color) => color,
|
Some(color) => color,
|
||||||
|
|
@ -336,6 +361,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update secondary color
|
/// Update secondary color
|
||||||
|
#[wasm_bindgen(js_name = updateSecondaryColor)]
|
||||||
pub fn update_secondary_color(&self, red: f32, green: f32, blue: f32, alpha: f32) -> Result<(), JsValue> {
|
pub fn update_secondary_color(&self, red: f32, green: f32, blue: f32, alpha: f32) -> Result<(), JsValue> {
|
||||||
let secondary_color = match Color::from_rgbaf32(red, green, blue, alpha) {
|
let secondary_color = match Color::from_rgbaf32(red, green, blue, alpha) {
|
||||||
Some(color) => color,
|
Some(color) => color,
|
||||||
|
|
@ -349,24 +375,28 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Paste layers from a serialized json representation
|
/// Paste layers from a serialized json representation
|
||||||
|
#[wasm_bindgen(js_name = pasteSerializedData)]
|
||||||
pub fn paste_serialized_data(&self, data: String) {
|
pub fn paste_serialized_data(&self, data: String) {
|
||||||
let message = PortfolioMessage::PasteSerializedData { data };
|
let message = PortfolioMessage::PasteSerializedData { data };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Modify the layer selection based on the layer which is clicked while holding down the <kbd>Ctrl</kbd> and/or <kbd>Shift</kbd> modifier keys used for range selection behavior
|
/// Modify the layer selection based on the layer which is clicked while holding down the <kbd>Ctrl</kbd> and/or <kbd>Shift</kbd> modifier keys used for range selection behavior
|
||||||
|
#[wasm_bindgen(js_name = selectLayer)]
|
||||||
pub fn select_layer(&self, layer_path: Vec<LayerId>, ctrl: bool, shift: bool) {
|
pub fn select_layer(&self, layer_path: Vec<LayerId>, ctrl: bool, shift: bool) {
|
||||||
let message = DocumentMessage::SelectLayer { layer_path, ctrl, shift };
|
let message = DocumentMessage::SelectLayer { layer_path, ctrl, shift };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deselect all layers
|
/// Deselect all layers
|
||||||
|
#[wasm_bindgen(js_name = deselectAllLayers)]
|
||||||
pub fn deselect_all_layers(&self) {
|
pub fn deselect_all_layers(&self) {
|
||||||
let message = DocumentMessage::DeselectAllLayers;
|
let message = DocumentMessage::DeselectAllLayers;
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move a layer to be next to the specified neighbor
|
/// Move a layer to be next to the specified neighbor
|
||||||
|
#[wasm_bindgen(js_name = moveLayerInTree)]
|
||||||
pub fn move_layer_in_tree(&self, folder_path: Vec<LayerId>, insert_index: isize) {
|
pub fn move_layer_in_tree(&self, folder_path: Vec<LayerId>, insert_index: isize) {
|
||||||
let message = DocumentMessage::MoveSelectedLayersTo {
|
let message = DocumentMessage::MoveSelectedLayersTo {
|
||||||
folder_path,
|
folder_path,
|
||||||
|
|
@ -377,24 +407,28 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the name for the layer
|
/// Set the name for the layer
|
||||||
|
#[wasm_bindgen(js_name = setLayerName)]
|
||||||
pub fn set_layer_name(&self, layer_path: Vec<LayerId>, name: String) {
|
pub fn set_layer_name(&self, layer_path: Vec<LayerId>, name: String) {
|
||||||
let message = DocumentMessage::SetLayerName { layer_path, name };
|
let message = DocumentMessage::SetLayerName { layer_path, name };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates document (in viewport coords)
|
/// Translates document (in viewport coords)
|
||||||
|
#[wasm_bindgen(js_name = translateCanvas)]
|
||||||
pub fn translate_canvas(&self, delta_x: f64, delta_y: f64) {
|
pub fn translate_canvas(&self, delta_x: f64, delta_y: f64) {
|
||||||
let message = NavigationMessage::TranslateCanvas { delta: (delta_x, delta_y).into() };
|
let message = NavigationMessage::TranslateCanvas { delta: (delta_x, delta_y).into() };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translates document (in viewport coords)
|
/// Translates document (in viewport coords)
|
||||||
|
#[wasm_bindgen(js_name = translateCanvasByFraction)]
|
||||||
pub fn translate_canvas_by_fraction(&self, delta_x: f64, delta_y: f64) {
|
pub fn translate_canvas_by_fraction(&self, delta_x: f64, delta_y: f64) {
|
||||||
let message = NavigationMessage::TranslateCanvasByViewportFraction { delta: (delta_x, delta_y).into() };
|
let message = NavigationMessage::TranslateCanvasByViewportFraction { delta: (delta_x, delta_y).into() };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends the blob url generated by js
|
/// Sends the blob url generated by js
|
||||||
|
#[wasm_bindgen(js_name = setImageBlobUrl)]
|
||||||
pub fn set_image_blob_url(&self, path: Vec<LayerId>, blob_url: String, width: f64, height: f64) {
|
pub fn set_image_blob_url(&self, path: Vec<LayerId>, blob_url: String, width: f64, height: f64) {
|
||||||
let dimensions = (width, height);
|
let dimensions = (width, height);
|
||||||
let message = Operation::SetImageBlobUrl { path, blob_url, dimensions };
|
let message = Operation::SetImageBlobUrl { path, blob_url, dimensions };
|
||||||
|
|
@ -402,6 +436,7 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pastes an image
|
/// Pastes an image
|
||||||
|
#[wasm_bindgen(js_name = pasteImage)]
|
||||||
pub fn paste_image(&self, mime: String, image_data: Vec<u8>, mouse_x: Option<f64>, mouse_y: Option<f64>) {
|
pub fn paste_image(&self, mime: String, image_data: Vec<u8>, mouse_x: Option<f64>, mouse_y: Option<f64>) {
|
||||||
let mouse = mouse_x.and_then(|x| mouse_y.map(|y| (x, y)));
|
let mouse = mouse_x.and_then(|x| mouse_y.map(|y| (x, y)));
|
||||||
let message = DocumentMessage::PasteImage { mime, image_data, mouse };
|
let message = DocumentMessage::PasteImage { mime, image_data, mouse };
|
||||||
|
|
@ -409,12 +444,14 @@ impl JsEditorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle visibility of a layer from the layer list
|
/// Toggle visibility of a layer from the layer list
|
||||||
|
#[wasm_bindgen(js_name = toggleLayerVisibility)]
|
||||||
pub fn toggle_layer_visibility(&self, layer_path: Vec<LayerId>) {
|
pub fn toggle_layer_visibility(&self, layer_path: Vec<LayerId>) {
|
||||||
let message = DocumentMessage::ToggleLayerVisibility { layer_path };
|
let message = DocumentMessage::ToggleLayerVisibility { layer_path };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Toggle expansions state of a layer from the layer list
|
/// Toggle expansions state of a layer from the layer list
|
||||||
|
#[wasm_bindgen(js_name = toggleLayerExpansion)]
|
||||||
pub fn toggle_layer_expansion(&self, layer_path: Vec<LayerId>) {
|
pub fn toggle_layer_expansion(&self, layer_path: Vec<LayerId>) {
|
||||||
let message = DocumentMessage::ToggleLayerExpansion { layer_path };
|
let message = DocumentMessage::ToggleLayerExpansion { layer_path };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ use std::collections::HashMap;
|
||||||
/// A font type (storing font family and font style and an optional preview URL)
|
/// A font type (storing font family and font style and an optional preview URL)
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
|
||||||
pub struct Font {
|
pub struct Font {
|
||||||
|
#[serde(rename = "fontFamily")]
|
||||||
pub font_family: String,
|
pub font_family: String,
|
||||||
|
#[serde(rename = "fontStyle")]
|
||||||
pub font_style: String,
|
pub font_style: String,
|
||||||
}
|
}
|
||||||
impl Font {
|
impl Font {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ title = "Features and roadmap"
|
||||||
template = "page.html"
|
template = "page.html"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
This page is a work in progress. Below is an incomplete list of planned features in no particular order. Many are long-term aspirations. In the coming days, this will be sequenced into a roadmap and expanded with further details.
|
This page is a work in progress. Below is an incomplete list of planned features in no particular order. Many are long-term aspirations. Soon, this will be sequenced into a roadmap and expanded with further details.
|
||||||
|
|
||||||
Short-term feature development at a more granular level is tracked in the [Task Board](https://github.com/GraphiteEditor/Graphite/projects/1) on GitHub. Check that out to see what's coming down the pipeline during monthly sprints.
|
Short-term feature development at a more granular level is tracked in the [Task Board](https://github.com/GraphiteEditor/Graphite/projects/1) on GitHub. Check that out to see what's coming down the pipeline during monthly sprints.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue