mirror of
https://github.com/penpot/penpot.git
synced 2026-01-19 11:50:40 -05:00
Compare commits
5 Commits
GlobalStar
...
staging-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f42ff27f3d | ||
|
|
89f40dcda2 | ||
|
|
ccac7bd510 | ||
|
|
43d1d127dc | ||
|
|
8bd3ef717c |
@@ -312,8 +312,8 @@
|
||||
[]
|
||||
(let [on-reload (mf/use-fn #(js/location.reload))]
|
||||
[:> error-container* {}
|
||||
[:div {:class (stl/css :main-message)} (tr "labels.webgl-context-lost.main-message")]
|
||||
[:div {:class (stl/css :desc-message)} (tr "labels.webgl-context-lost.desc-message")]
|
||||
[:div {:class (stl/css :main-message)} (tr "errors.webgl-context-lost.main-message")]
|
||||
[:div {:class (stl/css :desc-message)} (tr "errors.webgl-context-lost.desc-message")]
|
||||
[:div {:class (stl/css :buttons-container)}
|
||||
[:> button* {:variant "primary" :on-click on-reload}
|
||||
(tr "labels.reload-page")]]]))
|
||||
|
||||
@@ -275,7 +275,11 @@
|
||||
:wglobal wglobal
|
||||
:layout layout}])
|
||||
(when (or (not (and file-loaded? page-id))
|
||||
(and wasm-renderer-enabled? (not @first-frame-rendered?)))
|
||||
;; in wasm renderer, extend the pixel loader until the first frame is rendered
|
||||
;; but do not apply it when switching pages
|
||||
(and wasm-renderer-enabled?
|
||||
(not file-loaded?)
|
||||
(not @first-frame-rendered?)))
|
||||
[:> workspace-loader*])]]]]]]))
|
||||
|
||||
(mf/defc workspace-page*
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.title-bar :refer [title-bar*]]
|
||||
@@ -22,9 +23,11 @@
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.icons :as deprecated-icon]
|
||||
[app.main.ui.notifications.badge :refer [badge-notification]]
|
||||
[app.render-wasm.api :as wasm.api]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :as i18n :refer [tr]]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.timers :as timers]
|
||||
[cuerdas.core :as str]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
@@ -52,6 +55,8 @@
|
||||
refs/workspace-data
|
||||
=))
|
||||
|
||||
|
||||
|
||||
;; --- Page Item
|
||||
|
||||
(mf/defc page-item
|
||||
@@ -63,6 +68,22 @@
|
||||
navigate-fn (mf/use-fn (mf/deps id) #(st/emit! :interrupt (dcm/go-to-workspace :page-id id)))
|
||||
read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||
|
||||
on-click
|
||||
(mf/use-fn
|
||||
(mf/deps id)
|
||||
(fn []
|
||||
;; when using the wasm renderer, apply a blur effect to the viewport canvas
|
||||
(if (features/active-feature? @st/state "render-wasm/v1")
|
||||
(do
|
||||
(wasm.api/capture-canvas-pixels)
|
||||
(wasm.api/apply-canvas-blur)
|
||||
;; NOTE: it seems we need two RAF so the blur is actually applied and visible
|
||||
;; in the canvas :(
|
||||
(timers/raf
|
||||
(fn []
|
||||
(timers/raf navigate-fn))))
|
||||
(navigate-fn))))
|
||||
|
||||
on-delete
|
||||
(mf/use-fn
|
||||
(mf/deps id)
|
||||
@@ -155,7 +176,7 @@
|
||||
:selected selected?)
|
||||
:data-testid (dm/str "page-" id)
|
||||
:tab-index "0"
|
||||
:on-click navigate-fn
|
||||
:on-click on-click
|
||||
:on-double-click on-double-click
|
||||
:on-context-menu on-context-menu}
|
||||
[:div {:class (stl/css :page-icon)}
|
||||
|
||||
@@ -312,6 +312,11 @@
|
||||
(js/console.error "Error initializing canvas context:" e)
|
||||
false))]
|
||||
(reset! canvas-init? init?)
|
||||
(when init?
|
||||
;; Restore previous canvas pixels immediately after context initialization
|
||||
;; This happens before initialize-viewport is called
|
||||
(wasm.api/apply-canvas-blur)
|
||||
(wasm.api/restore-previous-canvas-pixels))
|
||||
(when-not init?
|
||||
(js/alert "WebGL not supported")
|
||||
(st/emit! (dcm/go-to-dashboard-recent))))))))
|
||||
@@ -340,6 +345,7 @@
|
||||
|
||||
(mf/with-effect [@canvas-init? zoom vbox background]
|
||||
(when (and @canvas-init? (not @initialized?))
|
||||
(wasm.api/clear-canvas-pixels)
|
||||
(wasm.api/initialize-viewport base-objects zoom vbox background)
|
||||
(reset! initialized? true)))
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
[app.main.worker :as mw]
|
||||
[app.render-wasm.api.fonts :as f]
|
||||
[app.render-wasm.api.texts :as t]
|
||||
[app.render-wasm.api.webgl :as webgl]
|
||||
[app.render-wasm.deserializers :as dr]
|
||||
[app.render-wasm.helpers :as h]
|
||||
[app.render-wasm.mem :as mem]
|
||||
@@ -37,7 +38,6 @@
|
||||
[app.render-wasm.serializers :as sr]
|
||||
[app.render-wasm.serializers.color :as sr-clr]
|
||||
[app.render-wasm.svg-filters :as svg-filters]
|
||||
;; FIXME: rename; confunsing name
|
||||
[app.render-wasm.wasm :as wasm]
|
||||
[app.util.debug :as dbg]
|
||||
[app.util.dom :as dom]
|
||||
@@ -279,30 +279,6 @@
|
||||
[string]
|
||||
(+ (count string) 1))
|
||||
|
||||
(defn- create-webgl-texture-from-image
|
||||
"Creates a WebGL texture from an HTMLImageElement or ImageBitmap and returns the texture object"
|
||||
[gl image-element]
|
||||
(let [texture (.createTexture ^js gl)]
|
||||
(.bindTexture ^js gl (.-TEXTURE_2D ^js gl) texture)
|
||||
(.texParameteri ^js gl (.-TEXTURE_2D ^js gl) (.-TEXTURE_WRAP_S ^js gl) (.-CLAMP_TO_EDGE ^js gl))
|
||||
(.texParameteri ^js gl (.-TEXTURE_2D ^js gl) (.-TEXTURE_WRAP_T ^js gl) (.-CLAMP_TO_EDGE ^js gl))
|
||||
(.texParameteri ^js gl (.-TEXTURE_2D ^js gl) (.-TEXTURE_MIN_FILTER ^js gl) (.-LINEAR ^js gl))
|
||||
(.texParameteri ^js gl (.-TEXTURE_2D ^js gl) (.-TEXTURE_MAG_FILTER ^js gl) (.-LINEAR ^js gl))
|
||||
(.texImage2D ^js gl (.-TEXTURE_2D ^js gl) 0 (.-RGBA ^js gl) (.-RGBA ^js gl) (.-UNSIGNED_BYTE ^js gl) image-element)
|
||||
(.bindTexture ^js gl (.-TEXTURE_2D ^js gl) nil)
|
||||
texture))
|
||||
|
||||
(defn- get-webgl-context
|
||||
"Gets the WebGL context from the WASM module"
|
||||
[]
|
||||
(when wasm/context-initialized?
|
||||
(let [gl-obj (unchecked-get wasm/internal-module "GL")]
|
||||
(when gl-obj
|
||||
;; Get the current WebGL context from Emscripten
|
||||
;; The GL object has a currentContext property that contains the context handle
|
||||
(let [current-ctx (.-currentContext ^js gl-obj)]
|
||||
(when current-ctx
|
||||
(.-GLctx ^js current-ctx)))))))
|
||||
|
||||
(defn- get-texture-id-for-gl-object
|
||||
"Registers a WebGL texture with Emscripten's GL object system and returns its ID"
|
||||
@@ -332,8 +308,8 @@
|
||||
(->> (retrieve-image url)
|
||||
(rx/map
|
||||
(fn [img]
|
||||
(when-let [gl (get-webgl-context)]
|
||||
(let [texture (create-webgl-texture-from-image gl img)
|
||||
(when-let [gl (webgl/get-webgl-context)]
|
||||
(let [texture (webgl/create-webgl-texture-from-image gl img)
|
||||
texture-id (get-texture-id-for-gl-object texture)
|
||||
width (.-width ^js img)
|
||||
height (.-height ^js img)
|
||||
@@ -1384,8 +1360,9 @@
|
||||
all-children
|
||||
(->> ids
|
||||
(mapcat #(cfh/get-children-with-self objects %)))]
|
||||
|
||||
(h/call wasm/internal-module "_init_shapes_pool" (count all-children))
|
||||
(run! (partial set-object objects) all-children)
|
||||
(run! set-object all-children)
|
||||
|
||||
(let [content (-> (calculate-bool* bool-type ids)
|
||||
(path.impl/path-data))]
|
||||
@@ -1448,6 +1425,12 @@
|
||||
|
||||
result)))
|
||||
|
||||
(defn apply-canvas-blur
|
||||
[]
|
||||
(when wasm/canvas
|
||||
(dom/set-style! wasm/canvas "filter" "blur(4px)")))
|
||||
|
||||
|
||||
(defn init-wasm-module
|
||||
[module]
|
||||
(let [default-fn (unchecked-get module "default")
|
||||
@@ -1469,3 +1452,8 @@
|
||||
(js/console.error cause)
|
||||
(p/resolved false)))))
|
||||
(p/resolved false))))
|
||||
|
||||
;; Re-export public WebGL functions
|
||||
(def capture-canvas-pixels webgl/capture-canvas-pixels)
|
||||
(def restore-previous-canvas-pixels webgl/restore-previous-canvas-pixels)
|
||||
(def clear-canvas-pixels webgl/clear-canvas-pixels)
|
||||
|
||||
166
frontend/src/app/render_wasm/api/webgl.cljs
Normal file
166
frontend/src/app/render_wasm/api/webgl.cljs
Normal file
@@ -0,0 +1,166 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.render-wasm.api.webgl
|
||||
"WebGL utilities for pixel capture and rendering"
|
||||
(:require
|
||||
[app.common.logging :as log]
|
||||
[app.render-wasm.wasm :as wasm]
|
||||
[app.util.dom :as dom]))
|
||||
|
||||
(defn get-webgl-context
|
||||
"Gets the WebGL context from the WASM module"
|
||||
[]
|
||||
(when wasm/context-initialized?
|
||||
(let [gl-obj (unchecked-get wasm/internal-module "GL")]
|
||||
(when gl-obj
|
||||
;; Get the current WebGL context from Emscripten
|
||||
;; The GL object has a currentContext property that contains the context handle
|
||||
(let [current-ctx (.-currentContext ^js gl-obj)]
|
||||
(when current-ctx
|
||||
(.-GLctx ^js current-ctx)))))))
|
||||
|
||||
(defn create-webgl-texture-from-image
|
||||
"Creates a WebGL texture from an HTMLImageElement or ImageBitmap and returns the texture object"
|
||||
[gl image-element]
|
||||
(let [texture (.createTexture ^js gl)]
|
||||
(.bindTexture ^js gl (.-TEXTURE_2D ^js gl) texture)
|
||||
(.texParameteri ^js gl (.-TEXTURE_2D ^js gl) (.-TEXTURE_WRAP_S ^js gl) (.-CLAMP_TO_EDGE ^js gl))
|
||||
(.texParameteri ^js gl (.-TEXTURE_2D ^js gl) (.-TEXTURE_WRAP_T ^js gl) (.-CLAMP_TO_EDGE ^js gl))
|
||||
(.texParameteri ^js gl (.-TEXTURE_2D ^js gl) (.-TEXTURE_MIN_FILTER ^js gl) (.-LINEAR ^js gl))
|
||||
(.texParameteri ^js gl (.-TEXTURE_2D ^js gl) (.-TEXTURE_MAG_FILTER ^js gl) (.-LINEAR ^js gl))
|
||||
(.texImage2D ^js gl (.-TEXTURE_2D ^js gl) 0 (.-RGBA ^js gl) (.-RGBA ^js gl) (.-UNSIGNED_BYTE ^js gl) image-element)
|
||||
(.bindTexture ^js gl (.-TEXTURE_2D ^js gl) nil)
|
||||
texture))
|
||||
|
||||
;; FIXME: temporary function until we are able to keep the same <canvas> across pages.
|
||||
(defn- draw-imagedata-to-webgl
|
||||
"Draws ImageData to a WebGL2 context by creating a texture"
|
||||
[gl image-data]
|
||||
(let [width (.-width ^js image-data)
|
||||
height (.-height ^js image-data)
|
||||
texture (.createTexture ^js gl)]
|
||||
;; Bind texture and set parameters
|
||||
(.bindTexture ^js gl (.-TEXTURE_2D ^js gl) texture)
|
||||
(.texParameteri ^js gl (.-TEXTURE_2D ^js gl) (.-TEXTURE_WRAP_S ^js gl) (.-CLAMP_TO_EDGE ^js gl))
|
||||
(.texParameteri ^js gl (.-TEXTURE_2D ^js gl) (.-TEXTURE_WRAP_T ^js gl) (.-CLAMP_TO_EDGE ^js gl))
|
||||
(.texParameteri ^js gl (.-TEXTURE_2D ^js gl) (.-TEXTURE_MIN_FILTER ^js gl) (.-LINEAR ^js gl))
|
||||
(.texParameteri ^js gl (.-TEXTURE_2D ^js gl) (.-TEXTURE_MAG_FILTER ^js gl) (.-LINEAR ^js gl))
|
||||
(.texImage2D ^js gl (.-TEXTURE_2D ^js gl) 0 (.-RGBA ^js gl) (.-RGBA ^js gl) (.-UNSIGNED_BYTE ^js gl) image-data)
|
||||
|
||||
;; Set up viewport
|
||||
(.viewport ^js gl 0 0 width height)
|
||||
|
||||
;; Vertex & Fragment shaders
|
||||
;; Since we are only calling this function once (on page switch), we don't need
|
||||
;; to cache the compiled shaders somewhere else (cannot be reused in a differen context).
|
||||
(let [vertex-shader-source "#version 300 es
|
||||
in vec2 a_position;
|
||||
in vec2 a_texCoord;
|
||||
out vec2 v_texCoord;
|
||||
void main() {
|
||||
gl_Position = vec4(a_position, 0.0, 1.0);
|
||||
v_texCoord = a_texCoord;
|
||||
}"
|
||||
fragment-shader-source "#version 300 es
|
||||
precision highp float;
|
||||
in vec2 v_texCoord;
|
||||
uniform sampler2D u_texture;
|
||||
out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = texture(u_texture, v_texCoord);
|
||||
}"
|
||||
vertex-shader (.createShader ^js gl (.-VERTEX_SHADER ^js gl))
|
||||
fragment-shader (.createShader ^js gl (.-FRAGMENT_SHADER ^js gl))
|
||||
program (.createProgram ^js gl)]
|
||||
(.shaderSource ^js gl vertex-shader vertex-shader-source)
|
||||
(.compileShader ^js gl vertex-shader)
|
||||
(when-not (.getShaderParameter ^js gl vertex-shader (.-COMPILE_STATUS ^js gl))
|
||||
(log/error :hint "Vertex shader compilation failed"
|
||||
:log (.getShaderInfoLog ^js gl vertex-shader)))
|
||||
|
||||
(.shaderSource ^js gl fragment-shader fragment-shader-source)
|
||||
(.compileShader ^js gl fragment-shader)
|
||||
(when-not (.getShaderParameter ^js gl fragment-shader (.-COMPILE_STATUS ^js gl))
|
||||
(log/error :hint "Fragment shader compilation failed"
|
||||
:log (.getShaderInfoLog ^js gl fragment-shader)))
|
||||
|
||||
(.attachShader ^js gl program vertex-shader)
|
||||
(.attachShader ^js gl program fragment-shader)
|
||||
(.linkProgram ^js gl program)
|
||||
|
||||
(if (.getProgramParameter ^js gl program (.-LINK_STATUS ^js gl))
|
||||
(do
|
||||
(.useProgram ^js gl program)
|
||||
|
||||
;; Create full-screen quad vertices (normalized device coordinates)
|
||||
(let [position-location (.getAttribLocation ^js gl program "a_position")
|
||||
texcoord-location (.getAttribLocation ^js gl program "a_texCoord")
|
||||
position-buffer (.createBuffer ^js gl)
|
||||
texcoord-buffer (.createBuffer ^js gl)
|
||||
positions #js [-1.0 -1.0 1.0 -1.0 -1.0 1.0 -1.0 1.0 1.0 -1.0 1.0 1.0]
|
||||
texcoords #js [0.0 0.0 1.0 0.0 0.0 1.0 0.0 1.0 1.0 0.0 1.0 1.0]]
|
||||
;; Set up position buffer
|
||||
(.bindBuffer ^js gl (.-ARRAY_BUFFER ^js gl) position-buffer)
|
||||
(.bufferData ^js gl (.-ARRAY_BUFFER ^js gl) (js/Float32Array. positions) (.-STATIC_DRAW ^js gl))
|
||||
(.enableVertexAttribArray ^js gl position-location)
|
||||
(.vertexAttribPointer ^js gl position-location 2 (.-FLOAT ^js gl) false 0 0)
|
||||
;; Set up texcoord buffer
|
||||
(.bindBuffer ^js gl (.-ARRAY_BUFFER ^js gl) texcoord-buffer)
|
||||
(.bufferData ^js gl (.-ARRAY_BUFFER ^js gl) (js/Float32Array. texcoords) (.-STATIC_DRAW ^js gl))
|
||||
(.enableVertexAttribArray ^js gl texcoord-location)
|
||||
(.vertexAttribPointer ^js gl texcoord-location 2 (.-FLOAT ^js gl) false 0 0)
|
||||
;; Set texture uniform
|
||||
(.activeTexture ^js gl (.-TEXTURE0 ^js gl))
|
||||
(.bindTexture ^js gl (.-TEXTURE_2D ^js gl) texture)
|
||||
(let [texture-location (.getUniformLocation ^js gl program "u_texture")]
|
||||
(.uniform1i ^js gl texture-location 0))
|
||||
|
||||
;; draw
|
||||
(.drawArrays ^js gl (.-TRIANGLES ^js gl) 0 6)
|
||||
|
||||
;; cleanup
|
||||
(.deleteBuffer ^js gl position-buffer)
|
||||
(.deleteBuffer ^js gl texcoord-buffer)
|
||||
(.deleteShader ^js gl vertex-shader)
|
||||
(.deleteShader ^js gl fragment-shader)
|
||||
(.deleteProgram ^js gl program)))
|
||||
(log/error :hint "Program linking failed"
|
||||
:log (.getProgramInfoLog ^js gl program)))
|
||||
|
||||
(.bindTexture ^js gl (.-TEXTURE_2D ^js gl) nil)
|
||||
(.deleteTexture ^js gl texture))))
|
||||
|
||||
(defn restore-previous-canvas-pixels
|
||||
"Restores previous canvas pixels into the new canvas"
|
||||
[]
|
||||
(when-let [previous-canvas-pixels wasm/canvas-pixels]
|
||||
(when-let [gl wasm/gl-context]
|
||||
(draw-imagedata-to-webgl gl previous-canvas-pixels)
|
||||
(set! wasm/canvas-pixels nil))))
|
||||
|
||||
(defn clear-canvas-pixels
|
||||
[]
|
||||
(when wasm/canvas
|
||||
(let [context wasm/gl-context]
|
||||
(.clearColor ^js context 0 0 0 0.0)
|
||||
(.clear ^js context (.-COLOR_BUFFER_BIT ^js context))
|
||||
(.clear ^js context (.-DEPTH_BUFFER_BIT ^js context))
|
||||
(.clear ^js context (.-STENCIL_BUFFER_BIT ^js context)))
|
||||
(dom/set-style! wasm/canvas "filter" "none")
|
||||
(set! wasm/canvas-pixels nil)))
|
||||
|
||||
(defn capture-canvas-pixels
|
||||
"Captures the pixels of the viewport canvas"
|
||||
[]
|
||||
(when wasm/canvas
|
||||
(let [context wasm/gl-context
|
||||
width (.-width wasm/canvas)
|
||||
height (.-height wasm/canvas)
|
||||
buffer (js/Uint8ClampedArray. (* width height 4))
|
||||
_ (.readPixels ^js context 0 0 width height (.-RGBA ^js context) (.-UNSIGNED_BYTE ^js context) buffer)
|
||||
image-data (js/ImageData. buffer width height)]
|
||||
(set! wasm/canvas-pixels image-data))))
|
||||
@@ -12,6 +12,8 @@
|
||||
|
||||
;; Reference to the HTML canvas element.
|
||||
(defonce canvas nil)
|
||||
;; Reference to the captured pixels of the canvas (for page switching effect)
|
||||
(defonce canvas-pixels nil)
|
||||
|
||||
;; Reference to the Emscripten GL context wrapper.
|
||||
(defonce gl-context-handle nil)
|
||||
@@ -56,3 +58,4 @@
|
||||
:stroke-linecap shared/RawStrokeLineCap
|
||||
:stroke-linejoin shared/RawStrokeLineJoin
|
||||
:fill-rule shared/RawFillRule})
|
||||
|
||||
|
||||
@@ -1559,6 +1559,12 @@ msgstr "Old password is incorrect"
|
||||
msgid "feedback.description"
|
||||
msgstr "Description"
|
||||
|
||||
msgid "errors.webgl-context-lost.main-message"
|
||||
msgstr "Oops! The canvas context was lost"
|
||||
|
||||
msgid "errors.webgl-context-lost.desc-message"
|
||||
msgstr "WebGL has stopped working. Please reload the page to reset it"
|
||||
|
||||
#: src/app/main/ui/settings/feedback.cljs:122
|
||||
msgid "feedback.description-placeholder"
|
||||
msgstr "Please describe the reason of your feedback"
|
||||
@@ -8478,12 +8484,6 @@ msgstr "Recent"
|
||||
msgid "labels.deleted"
|
||||
msgstr "Deleted"
|
||||
|
||||
msgid "labels.webgl-context-lost.main-message"
|
||||
msgstr "Oops! The canvas context was lost"
|
||||
|
||||
msgid "labels.webgl-context-lost.desc-message"
|
||||
msgstr "WebGL has stopped working. Please reload the page to reset it"
|
||||
|
||||
msgid "dashboard.restore-all-deleted-button"
|
||||
msgstr "Restore All"
|
||||
|
||||
|
||||
@@ -1552,6 +1552,12 @@ msgstr "El email o la contraseña son incorrectos."
|
||||
msgid "errors.wrong-old-password"
|
||||
msgstr "La contraseña anterior no es correcta"
|
||||
|
||||
msgid "errors.webgl-context-lost.main-message"
|
||||
msgstr "Ups! Se ha perdido el contexto del canvas"
|
||||
|
||||
msgid "errors.webgl-context-lost.desc-message"
|
||||
msgstr "WebGL ha dejado de funcionar. Por favor, recarga la página para restaurarlo"
|
||||
|
||||
#: src/app/main/ui/settings/feedback.cljs:120
|
||||
msgid "feedback.description"
|
||||
msgstr "Descripción"
|
||||
@@ -8331,12 +8337,6 @@ msgstr "Recientes"
|
||||
msgid "labels.deleted"
|
||||
msgstr "Eliminados"
|
||||
|
||||
msgid "labels.webgl-context-lost.main-message"
|
||||
msgstr "Ups! Se ha perdido el contexto del canvas"
|
||||
|
||||
msgid "labels.webgl-context-lost.desc-message"
|
||||
msgstr "WebGL ha dejado de funcionar. Por favor, recarga la página para restaurarlo"
|
||||
|
||||
msgid "dashboard.restore-all-deleted-button"
|
||||
msgstr "Restaurar todo"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user