🎉 Show modal when WebGL is not available

This commit is contained in:
Belén Albeza 2026-03-30 17:18:29 +02:00
parent e99b6ec213
commit 0558bab092
6 changed files with 227 additions and 20 deletions

View File

@ -40,6 +40,7 @@
[app.main.ui.workspace.tokens.settings]
[app.main.ui.workspace.tokens.themes.create-modal]
[app.main.ui.workspace.viewport :refer [viewport*]]
[app.main.ui.workspace.webgl-unavailable-modal]
[app.util.debug :as dbg]
[app.util.dom :as dom]
[app.util.globals :as globals]

View File

@ -16,7 +16,7 @@
[app.common.types.path :as path]
[app.common.types.shape :as cts]
[app.common.types.shape.layout :as ctl]
[app.main.data.common :as dcm]
[app.main.data.modal :as modal]
[app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.variants :as dwv]
[app.main.features :as features]
@ -319,25 +319,50 @@
;; harder to follow through.
(mf/with-effect [page-id]
(when-let [canvas (mf/ref-val canvas-ref)]
(->> wasm.api/module
(p/fmap (fn [ready?]
(when ready?
(let [init? (try
(wasm.api/init-canvas-context canvas)
(catch :default e
(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))))))))
(fn []
(wasm.api/clear-canvas))))
(let [timeout-id-ref (volatile! nil)
unmounted? (volatile! false)
modal-shown? (volatile! false)
show-unavailable
(fn []
(when-not (or @unmounted? @modal-shown?)
(vreset! modal-shown? true)
(reset! canvas-init? false)
(st/emit! (modal/show {:type :webgl-unavailable}))))
try-init
(fn try-init [retries]
(when-not @unmounted?
(let [init? (try
(wasm.api/init-canvas-context canvas)
(catch :default e
(js/console.error "Error initializing canvas context:" e)
false))]
(cond
init?
(do
(reset! canvas-init? true)
;; 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))
(pos? retries)
(vreset! timeout-id-ref
(js/setTimeout #(try-init (dec retries)) 200))
:else
(show-unavailable)))))]
(reset! canvas-init? false)
(->> wasm.api/module
(p/fmap (fn [ready?]
(when ready?
(try-init 3)))))
(fn []
(vreset! unmounted? true)
(when-let [timeout-id @timeout-id-ref]
(js/clearTimeout timeout-id))
(wasm.api/clear-canvas)))))
(mf/with-effect [show-text-editor? workspace-editor-state edition]
(let [active-editor-state (get workspace-editor-state edition)]

View File

@ -0,0 +1,78 @@
;; 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.main.ui.workspace.webgl-unavailable-modal
(:require-macros [app.main.style :as stl])
(:require
[app.main.data.common :as dcm]
[app.main.data.modal :as modal]
[app.main.store :as st]
[app.main.ui.ds.buttons.button :refer [button*]]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.ds.foundations.typography :as t]
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
[app.main.ui.ds.foundations.typography.text :refer [text*]]
[app.util.dom :as dom]
[app.util.i18n :refer [tr]]
[app.util.keyboard :as k]
[goog.events :as events]
[rumext.v2 :as mf])
(:import goog.events.EventType))
(defn- close-and-go-dashboard
[]
(st/emit! (modal/hide)
(dcm/go-to-dashboard-recent)))
(def ^:const webgl-troubleshooting-url "https://help.penpot.app/user-guide/first-steps/troubleshooting-webgl/")
#_{:clojure-lsp/ignore [:clojure-lsp/unused-public-var]}
(mf/defc webgl-unavailable-modal*
{::mf/register modal/components
::mf/register-as :webgl-unavailable}
[_]
(let [handle-keydown (fn [event]
(when (k/esc? event)
(dom/stop-propagation event)
(close-and-go-dashboard)))]
(mf/with-effect []
(let [key (events/listen js/document EventType.KEYDOWN handle-keydown)]
(fn []
(events/unlistenByKey key))))
[:div {:class (stl/css :modal-overlay)}
[:div {:class (stl/css :modal-dialog)}
[:header {:class (stl/css :modal-header)}
[:> icon-button* {:on-click close-and-go-dashboard
:class (stl/css :modal-close-btn)
:icon i/close
:variant "action"
:size "medium"
:aria-label (tr "labels.close")}]
[:> heading* {:level 2 :typography t/title-medium}
(tr "webgl.modals.webgl-unavailable.title")]]
[:section {:class (stl/css :modal-content)}
[:> text* {:as "p" :typography t/body-large}
(tr "webgl.modals.webgl-unavailable.message")]
[:hr {:class (stl/css :modal-divider)}]
[:> text* {:as "p" :typography t/body-medium}
(tr "webgl.modals.webgl-unavailable.troubleshooting.before")
[:a {:href webgl-troubleshooting-url
:target "_blank"
:rel "noopener noreferrer"
:class (stl/css :link)}
(tr "webgl.modals.webgl-unavailable.troubleshooting.link")]
(tr "webgl.modals.webgl-unavailable.troubleshooting.after")]]
[:footer {:class (stl/css :modal-footer)}
[:> button* {:on-click close-and-go-dashboard
:variant "secondary"}
(tr "webgl.modals.webgl-unavailable.cta-dashboard")]
[:> button* {:to webgl-troubleshooting-url :target "_blank" :variant "primary"}
(tr "webgl.modals.webgl-unavailable.cta-troubleshooting")]]]]))

View File

@ -0,0 +1,61 @@
// 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
@use "ds/_utils.scss" as *;
@use "ds/_borders.scss" as *;
@use "refactor/common-refactor.scss" as deprecated;
.modal-overlay {
@extend .modal-overlay-base;
}
.modal-dialog {
@extend .modal-container-base;
color: var(--color-foreground-secondary);
display: grid;
gap: var(--sp-s);
padding: px2rem(72); // FIXME: This should be a token
max-width: px2rem(682); // FIXME: This should be a token
}
.modal-header {
color: var(--color-foreground-primary);
}
.modal-close-btn {
position: absolute;
top: px2rem(38); // FIXME: This should be a token
right: px2rem(38); // FIXME: This should be a token
}
.modal-content {
display: grid;
grid-template-rows: auto;
gap: var(--sp-s);
& > * {
margin: 0; // FIXME: This should be in reset styles
}
}
.modal-divider {
border: $b-1 solid var(--color-background-quaternary);
margin: var(--sp-xs) 0;
}
.modal-footer {
margin-block-start: var(--sp-l);
justify-self: end;
display: flex;
flex-direction: row;
gap: var(--sp-s);
}
.link {
color: var(--color-accent-primary);
}

View File

@ -8907,6 +8907,27 @@ msgstr ""
msgid "workspace.versions.warning.text"
msgstr "Autosaved versions will be kept for %s days."
msgid "webgl.modals.webgl-unavailable.title"
msgstr "Oops! WebGL is not available"
msgid "webgl.modals.webgl-unavailable.message"
msgstr "WebGL is not available in your browser, which is required for Penpot to work. Please check your browser settings and/or close graphics-heavy tabs."
msgid "webgl.modals.webgl-unavailable.troubleshooting.before"
msgstr "Follow our "
msgid "webgl.modals.webgl-unavailable.troubleshooting.link"
msgstr "WebGL troubleshooting guide"
msgid "webgl.modals.webgl-unavailable.troubleshooting.after"
msgstr " to check browser settings, GPU acceleration, drivers, and common system issues."
msgid "webgl.modals.webgl-unavailable.cta-dashboard"
msgstr "Go to dashboard"
msgid "webgl.modals.webgl-unavailable.cta-troubleshooting"
msgstr "Troubleshooting guide"
#, unused
msgid "workspace.viewport.click-to-close-path"
msgstr "Click to close the path"

View File

@ -8755,6 +8755,27 @@ msgstr "Si quieres aumentar este límite, contáctanos en [support@penpot.app](%
msgid "workspace.versions.warning.text"
msgstr "Los autoguardados duran %s días."
msgid "webgl.modals.webgl-unavailable.title"
msgstr "Vaya, WebGL no está disponible"
msgid "webgl.modals.webgl-unavailable.message"
msgstr "WebGL no está disponible en tu navegador, y es necesario para que Penpot funcione. Revisa la configuración de tu navegador y/o cierra pestañas con uso intensivo de gráficos."
msgid "webgl.modals.webgl-unavailable.troubleshooting.before"
msgstr "Consulta nuestra "
msgid "webgl.modals.webgl-unavailable.troubleshooting.link"
msgstr "guía de solución de problemas de WebGL"
msgid "webgl.modals.webgl-unavailable.troubleshooting.after"
msgstr " para revisar la configuración del navegador, la aceleración por GPU, los drivers y problemas comunes del sistema."
msgid "webgl.modals.webgl-unavailable.cta-dashboard"
msgstr "Ir al panel"
msgid "webgl.modals.webgl-unavailable.cta-troubleshooting"
msgstr "Guía de solución de problemas"
#, unused
msgid "workspace.viewport.click-to-close-path"
msgstr "Pulsar para cerrar la ruta"