diff --git a/CHANGES.md b/CHANGES.md
index b59234b7d3..826de71889 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -35,6 +35,14 @@
### :bug: Bugs fixed
- Fix incorrect handling of version restore operation [Github #9041](https://github.com/penpot/penpot/pull/9041)
+
+
+## 2.14.4
+
+### :bug: Bugs fixed
+
+- Fix email validation [Taiga #14006](https://tree.taiga.io/project/penpot/issue/14006)
+- Fix email blacklisting [Github #9122](https://github.com/penpot/penpot/pull/9122)
- Fix removeChild errors from unmount race conditions [Github #8927](https://github.com/penpot/penpot/pull/8927)
diff --git a/backend/src/app/email/blacklist.clj b/backend/src/app/email/blacklist.clj
index ca80afb6c9..a07dfccf91 100644
--- a/backend/src/app/email/blacklist.clj
+++ b/backend/src/app/email/blacklist.clj
@@ -36,10 +36,18 @@
:cause cause)))))
(defn contains?
- "Check if email is in the blacklist."
+ "Check if email is in the blacklist. Also matches subdomains: if
+ 'somedomain.com' is blacklisted, 'xxx@foo.somedomain.com' will also
+ be rejected."
[{:keys [::email/blacklist]} email]
- (let [[_ domain] (str/split email "@" 2)]
- (c/contains? blacklist (str/lower domain))))
+ (let [[_ domain] (str/split email "@" 2)
+ parts (str/split (str/lower domain) #"\.")]
+ (loop [parts parts]
+ (if (empty? parts)
+ false
+ (if (c/contains? blacklist (str/join "." parts))
+ true
+ (recur (rest parts)))))))
(defn enabled?
"Check if the blacklist is enabled"
diff --git a/backend/test/backend_tests/email_blacklist_test.clj b/backend/test/backend_tests/email_blacklist_test.clj
new file mode 100644
index 0000000000..5cc043fe32
--- /dev/null
+++ b/backend/test/backend_tests/email_blacklist_test.clj
@@ -0,0 +1,34 @@
+;; 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 backend-tests.email-blacklist-test
+ (:require
+ [app.email :as-alias email]
+ [app.email.blacklist :as blacklist]
+ [clojure.test :as t]))
+
+(def ^:private cfg
+ {::email/blacklist #{"somedomain.com" "spam.net"}})
+
+(t/deftest test-exact-domain-match
+ (t/is (true? (blacklist/contains? cfg "user@somedomain.com")))
+ (t/is (true? (blacklist/contains? cfg "user@spam.net")))
+ (t/is (false? (blacklist/contains? cfg "user@legit.com"))))
+
+(t/deftest test-subdomain-match
+ (t/is (true? (blacklist/contains? cfg "user@sub.somedomain.com")))
+ (t/is (true? (blacklist/contains? cfg "user@a.b.somedomain.com")))
+ ;; A domain that merely contains the blacklisted string but is not a
+ ;; subdomain must NOT be rejected.
+ (t/is (false? (blacklist/contains? cfg "user@notsomedomain.com"))))
+
+(t/deftest test-case-insensitive
+ (t/is (true? (blacklist/contains? cfg "user@SOMEDOMAIN.COM")))
+ (t/is (true? (blacklist/contains? cfg "user@Sub.SomeDomain.Com"))))
+
+(t/deftest test-non-blacklisted-domain
+ (t/is (false? (blacklist/contains? cfg "user@example.com")))
+ (t/is (false? (blacklist/contains? cfg "user@sub.legit.com"))))
diff --git a/common/src/app/common/spec.cljc b/common/src/app/common/spec.cljc
index 38af563499..d6f0d6cacc 100644
--- a/common/src/app/common/spec.cljc
+++ b/common/src/app/common/spec.cljc
@@ -113,12 +113,19 @@
(tgen/fmap keyword)))))
;; --- SPEC: email
+;;
+;; Regex rules enforced:
+;; local part - valid RFC chars, no leading/trailing dot, no consecutive dots
+;; domain - labels can't start/end with hyphen, no empty labels
+;; TLD - at least 2 alphabetic chars
-(def email-re #"[a-zA-Z0-9_.+-\\\\]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+")
+(def email-re
+ #"^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,63}$")
(defn parse-email
[s]
- (some->> s (re-seq email-re) first))
+ (when (and (string? s) (re-matches email-re s))
+ s))
(letfn [(conformer [v]
(or (parse-email v) ::s/invalid))
@@ -126,11 +133,10 @@
(dm/str v))]
(s/def ::email
(s/with-gen (s/conformer conformer unformer)
- #(as-> (tgen/let [p1 (s/gen ::not-empty-string)
- p2 (s/gen ::not-empty-string)
- p3 (tgen/elements ["com" "net"])]
- (str p1 "@" p2 "." p3)) $
- (tgen/such-that (partial re-matches email-re) $ 50)))))
+ #(tgen/let [local (tgen/string-alphanumeric 1 20)
+ label (tgen/string-alphanumeric 2 10)
+ tld (tgen/elements ["com" "net" "org" "io" "co" "dev"])]
+ (str local "@" label "." tld)))))
;; -- SPEC: uri
diff --git a/common/test/common_tests/runner.cljc b/common/test/common_tests/runner.cljc
index e8fd6ac9a9..06f7926c47 100644
--- a/common/test/common_tests/runner.cljc
+++ b/common/test/common_tests/runner.cljc
@@ -55,6 +55,7 @@
[common-tests.path-names-test]
[common-tests.record-test]
[common-tests.schema-test]
+ [common-tests.spec-test]
[common-tests.svg-path-test]
[common-tests.svg-test]
[common-tests.text-test]
@@ -134,6 +135,7 @@
'common-tests.path-names-test
'common-tests.record-test
'common-tests.schema-test
+ 'common-tests.spec-test
'common-tests.svg-path-test
'common-tests.svg-test
'common-tests.text-test
diff --git a/common/test/common_tests/spec_test.cljc b/common/test/common_tests/spec_test.cljc
new file mode 100644
index 0000000000..425f7f8066
--- /dev/null
+++ b/common/test/common_tests/spec_test.cljc
@@ -0,0 +1,89 @@
+;; 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 common-tests.spec-test
+ (:require
+ [app.common.spec :as spec]
+ [clojure.test :as t]))
+
+(t/deftest valid-emails
+ (t/testing "accepts well-formed email addresses"
+ (doseq [email ["user@domain.com"
+ "user.name@domain.com"
+ "user+tag@domain.com"
+ "user-name@domain.com"
+ "user_name@domain.com"
+ "user123@domain.com"
+ "USER@DOMAIN.COM"
+ "u@domain.io"
+ "user@sub.domain.com"
+ "user@domain.co.uk"
+ "user@domain.dev"
+ "a@bc.co"]]
+ (t/is (some? (spec/parse-email email)) (str "should accept: " email)))))
+
+(t/deftest rejects-invalid-local-part
+ (t/testing "rejects local part starting with a dot"
+ (t/is (nil? (spec/parse-email ".user@domain.com"))))
+
+ (t/testing "rejects local part with consecutive dots"
+ (t/is (nil? (spec/parse-email "user..name@domain.com"))))
+
+ (t/testing "rejects local part with spaces"
+ (t/is (nil? (spec/parse-email "us er@domain.com"))))
+
+ (t/testing "rejects local part with comma"
+ (t/is (nil? (spec/parse-email "user,name@domain.com")))
+ (t/is (nil? (spec/parse-email ",user@domain.com"))))
+
+ (t/testing "rejects empty local part"
+ (t/is (nil? (spec/parse-email "@domain.com")))))
+
+(t/deftest rejects-invalid-domain
+ (t/testing "rejects domain starting with a dot"
+ (t/is (nil? (spec/parse-email "user@.domain.com"))))
+
+ (t/testing "rejects domain part with comma"
+ (t/is (nil? (spec/parse-email "user@domain,com")))
+ (t/is (nil? (spec/parse-email "user@,domain.com"))))
+
+ (t/testing "rejects domain with consecutive dots"
+ (t/is (nil? (spec/parse-email "user@sub..domain.com"))))
+
+ (t/testing "rejects label starting with hyphen"
+ (t/is (nil? (spec/parse-email "user@-domain.com"))))
+
+ (t/testing "rejects label ending with hyphen"
+ (t/is (nil? (spec/parse-email "user@domain-.com"))))
+
+ (t/testing "rejects TLD shorter than 2 chars"
+ (t/is (nil? (spec/parse-email "user@domain.c"))))
+
+ (t/testing "rejects domain without a dot"
+ (t/is (nil? (spec/parse-email "user@domain"))))
+
+ (t/testing "rejects domain with spaces"
+ (t/is (nil? (spec/parse-email "user@do main.com"))))
+
+ (t/testing "rejects domain ending with a dot"
+ (t/is (nil? (spec/parse-email "user@domain.")))))
+
+(t/deftest rejects-invalid-structure
+ (t/testing "rejects nil"
+ (t/is (nil? (spec/parse-email nil))))
+
+ (t/testing "rejects empty string"
+ (t/is (nil? (spec/parse-email ""))))
+
+ (t/testing "rejects string without @"
+ (t/is (nil? (spec/parse-email "userdomain.com"))))
+
+ (t/testing "rejects string with multiple @"
+ (t/is (nil? (spec/parse-email "user@@domain.com")))
+ (t/is (nil? (spec/parse-email "us@er@domain.com"))))
+
+ (t/testing "rejects empty domain"
+ (t/is (nil? (spec/parse-email "user@")))))
diff --git a/docker/images/docker-compose.yaml b/docker/images/docker-compose.yaml
index 5d3b84d09c..b4ecc2b41d 100644
--- a/docker/images/docker-compose.yaml
+++ b/docker/images/docker-compose.yaml
@@ -105,7 +105,7 @@ services:
# - "traefik.http.routers.penpot-https.tls=true"
environment:
- << : [*penpot-flags, *penpot-http-body-size]
+ << : [*penpot-flags, *penpot-http-body-size, *penpot-public-uri]
penpot-backend:
image: "penpotapp/backend:${PENPOT_VERSION:-latest}"
diff --git a/docker/images/files/nginx-entrypoint.sh b/docker/images/files/nginx-entrypoint.sh
index 4512d06495..9ce2b9261d 100644
--- a/docker/images/files/nginx-entrypoint.sh
+++ b/docker/images/files/nginx-entrypoint.sh
@@ -19,6 +19,10 @@ update_flags() {
-e "s|^//var penpotFlags = .*;|var penpotFlags = \"$PENPOT_FLAGS\";|g" \
"$1")" > "$1"
fi
+
+ if [ -n "$PENPOT_PUBLIC_URI" ]; then
+ echo "var penpotPublicURI = \"$PENPOT_PUBLIC_URI\";" >> "$1";
+ fi
}
update_flags /var/www/app/js/config.js
@@ -30,8 +34,9 @@ update_flags /var/www/app/js/config.js
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060}
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061}
export PENPOT_NITRATE_URI=${PENPOT_NITRATE_URI:-http://penpot-nitrate:3000}
+export PENPOT_MCP_URI=${PENPOT_MCP_URI:-http://penpot-mcp}
export PENPOT_HTTP_SERVER_MAX_BODY_SIZE=${PENPOT_HTTP_SERVER_MAX_BODY_SIZE:-367001600} # Default to 350MiB
-envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_NITRATE_URI,\$PENPOT_HTTP_SERVER_MAX_BODY_SIZE" \
+envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_NITRATE_URI,\$PENPOT_MCP_URI,\$PENPOT_HTTP_SERVER_MAX_BODY_SIZE" \
< /tmp/nginx.conf.template > /etc/nginx/nginx.conf
PENPOT_DEFAULT_INTERNAL_RESOLVER="$(awk 'BEGIN{ORS=" "} $1=="nameserver" { sub(/%.*$/,"",$2); print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf)"
diff --git a/docker/images/files/nginx.conf.template b/docker/images/files/nginx.conf.template
index d0b7bc3b1f..0daab4b9d4 100644
--- a/docker/images/files/nginx.conf.template
+++ b/docker/images/files/nginx.conf.template
@@ -135,6 +135,23 @@ http {
proxy_http_version 1.1;
}
+ location /mcp/ws {
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection 'upgrade';
+ proxy_pass $PENPOT_MCP_URI:4402;
+ proxy_http_version 1.1;
+ }
+
+ location /mcp/stream {
+ proxy_pass $PENPOT_MCP_URI:4401/mcp;
+ proxy_http_version 1.1;
+ }
+
+ location /mcp/sse {
+ proxy_pass $PENPOT_MCP_URI:4401/sse;
+ proxy_http_version 1.1;
+ }
+
location /readyz {
access_log off;
proxy_pass $PENPOT_BACKEND_URI$request_uri;
diff --git a/exporter/src/app/renderer/bitmap.cljs b/exporter/src/app/renderer/bitmap.cljs
index 00c67ba508..6b9dbcb4b9 100644
--- a/exporter/src/app/renderer/bitmap.cljs
+++ b/exporter/src/app/renderer/bitmap.cljs
@@ -62,6 +62,7 @@
:wasm (when is-wasm "true")
:scale scale}
uri (-> (cf/get :public-uri)
- (assoc :path "/render.html")
+ (u/ensure-path-slash)
+ (u/join "render.html")
(assoc :query (u/map->query-string params)))]
(bw/exec! (prepare-options uri) (partial render uri)))))
diff --git a/exporter/src/app/renderer/pdf.cljs b/exporter/src/app/renderer/pdf.cljs
index 25bcfc036b..edfdcda1b1 100644
--- a/exporter/src/app/renderer/pdf.cljs
+++ b/exporter/src/app/renderer/pdf.cljs
@@ -35,7 +35,7 @@
:object-id object-id
:route "objects"}]
(-> base-uri
- (assoc :path "/render.html")
+ (u/join "render.html")
(assoc :query (u/map->query-string params)))))
(sync-page-size! [dom]
@@ -76,6 +76,7 @@
(on-object (assoc object :path path))
(p/recur (rest objects))))))]
- (let [base-uri (cf/get :public-uri)]
+ (let [base-uri (-> (cf/get :public-uri)
+ (u/ensure-path-slash))]
(bw/exec! (prepare-options base-uri)
(partial render base-uri)))))
diff --git a/exporter/src/app/renderer/svg.cljs b/exporter/src/app/renderer/svg.cljs
index 73558dbe5f..71da424fb3 100644
--- a/exporter/src/app/renderer/svg.cljs
+++ b/exporter/src/app/renderer/svg.cljs
@@ -349,7 +349,8 @@
:object-id (mapv :id objects)
:route "objects"}
uri (-> (cf/get :public-uri)
- (assoc :path "/render.html")
+ (u/ensure-path-slash)
+ (u/join "render.html")
(assoc :query (u/map->query-string params)))]
(bw/exec! (prepare-options uri)
(partial render uri)))))
diff --git a/frontend/resources/styles/common/refactor/basic-rules.scss b/frontend/resources/styles/common/refactor/basic-rules.scss
index c82907a5b6..91068275cc 100644
--- a/frontend/resources/styles/common/refactor/basic-rules.scss
+++ b/frontend/resources/styles/common/refactor/basic-rules.scss
@@ -800,7 +800,7 @@
position: absolute;
padding: $s-4;
border-radius: $br-8;
- z-index: $z-index-10;
+ z-index: $z-index-dropdown;
color: var(--title-foreground-color-hover);
background-color: var(--menu-background-color);
border: $s-2 solid var(--panel-border-color);
diff --git a/frontend/resources/styles/common/refactor/z-index.scss b/frontend/resources/styles/common/refactor/z-index.scss
index 755b2e9fad..3d36cb37f5 100644
--- a/frontend/resources/styles/common/refactor/z-index.scss
+++ b/frontend/resources/styles/common/refactor/z-index.scss
@@ -11,5 +11,5 @@ $z-index-4: 4; // context menu
$z-index-5: 5; // modal
$z-index-10: 10;
$z-index-20: 20;
-$z-index-modal: 30; // When refactor finish we can reduce this number,
-$z-index-alert: 40; // When refactor finish we can reduce this number,
+$z-index-modal: 300;
+$z-index-dropdown: 400;
diff --git a/frontend/resources/templates/index.mustache b/frontend/resources/templates/index.mustache
index f80b7e7759..60c6119fd0 100644
--- a/frontend/resources/templates/index.mustache
+++ b/frontend/resources/templates/index.mustache
@@ -31,7 +31,6 @@
globalThis.penpotVersion = "{{& version}}";
globalThis.penpotVersionTag = "{{& version_tag}}";
globalThis.penpotBuildDate = "{{& build_date}}";
- globalThis.penpotWorkerURI = "{{& manifest.worker_main}}";
{{# manifest}}
diff --git a/frontend/resources/templates/rasterizer.mustache b/frontend/resources/templates/rasterizer.mustache
index 90a7f1dfdc..6a3d815e29 100644
--- a/frontend/resources/templates/rasterizer.mustache
+++ b/frontend/resources/templates/rasterizer.mustache
@@ -9,7 +9,6 @@
globalThis.penpotVersion = "{{& version}}";
globalThis.penpotVersionTag = "{{& version_tag}}";
globalThis.penpotBuildDate = "{{& build_date}}";
- globalThis.penpotWorkerURI = "{{& manifest.worker_main}}";
{{# manifest}}
diff --git a/frontend/resources/templates/render.mustache b/frontend/resources/templates/render.mustache
index 4de213f9ad..67629b075e 100644
--- a/frontend/resources/templates/render.mustache
+++ b/frontend/resources/templates/render.mustache
@@ -14,7 +14,7 @@
{{# manifest}}
-
+
{{/manifest}}
diff --git a/frontend/scripts/_helpers.js b/frontend/scripts/_helpers.js
index d837b93e9d..c6396f99bc 100644
--- a/frontend/scripts/_helpers.js
+++ b/frontend/scripts/_helpers.js
@@ -207,9 +207,9 @@ async function generateManifest() {
rasterizer_main: "./js/rasterizer.js",
config: "./js/config.js?version=" + VERSION_TAG,
+ config_render: "./js/config-render.js?version=" + VERSION_TAG,
polyfills: "./js/polyfills.js?version=" + VERSION_TAG,
libs: "./js/libs.js?version=" + VERSION_TAG,
- worker_main: "./js/worker/main.js?version=" + VERSION_TAG,
default_translations: "./js/translation.en.js?version=" + VERSION_TAG,
importmap: JSON.stringify({
diff --git a/frontend/src/app/config.cljs b/frontend/src/app/config.cljs
index 11c59e5a65..058e265bd2 100644
--- a/frontend/src/app/config.cljs
+++ b/frontend/src/app/config.cljs
@@ -160,9 +160,9 @@
(def privacy-policy-uri (obj/get global "penpotPrivacyPolicyURI"))
(def flex-help-uri (obj/get global "penpotGridHelpURI" "https://help.penpot.app/user-guide/flexible-layouts/"))
(def grid-help-uri (obj/get global "penpotGridHelpURI" "https://help.penpot.app/user-guide/flexible-layouts/"))
-(def plugins-list-uri (obj/get global "penpotPluginsListUri" "https://penpot.app/penpothub/plugins"))
+(def plugins-list-uri (obj/get global "penpotPluginsListURI" "https://penpot.app/penpothub/plugins"))
(def plugins-whitelist (into #{} (obj/get global "penpotPluginsWhitelist" [])))
-(def templates-uri (obj/get global "penpotTemplatesUri" "https://penpot.github.io/penpot-files/"))
+(def templates-uri (obj/get global "penpotTemplatesURI" "https://penpot.github.io/penpot-files/"))
(def upload-chunk-size (obj/get global "penpotUploadChunkSize" (* 1024 1024 25))) ;; 25 MiB
;; We set the current parsed flags under common for make
@@ -189,7 +189,10 @@
public-uri))
(def worker-uri
- (obj/get global "penpotWorkerURI" "/js/worker/main.js"))
+ (-> public-uri
+ (u/join "js/worker/main.js")
+ (get :path)
+ (str "?version=" version-tag)))
(defn external-feature-flag
[flag value]
diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs
index 48dc9529cd..c61307cf93 100644
--- a/frontend/src/app/main/refs.cljs
+++ b/frontend/src/app/main/refs.cljs
@@ -648,3 +648,9 @@
(def progress
(l/derived :progress st/state))
+
+(def access-tokens
+ (l/derived :access-tokens st/state))
+
+(def access-token-created
+ (l/derived :access-token-created st/state))
diff --git a/frontend/src/app/main/ui/components/context_menu_a11y.scss b/frontend/src/app/main/ui/components/context_menu_a11y.scss
index e0fc29989e..787941b595 100644
--- a/frontend/src/app/main/ui/components/context_menu_a11y.scss
+++ b/frontend/src/app/main/ui/components/context_menu_a11y.scss
@@ -5,12 +5,13 @@
// Copyright (c) KALEIDOS INC
@use "refactor/common-refactor.scss" as deprecated;
+@use "ds/z-index.scss" as *;
.context-menu {
position: relative;
visibility: hidden;
opacity: deprecated.$op-0;
- z-index: deprecated.$z-index-4;
+ z-index: var(--z-index-dropdown);
&.is-open {
position: relative;
diff --git a/frontend/src/app/main/ui/dashboard/deleted.scss b/frontend/src/app/main/ui/dashboard/deleted.scss
index 8a04eda993..7187633722 100644
--- a/frontend/src/app/main/ui/dashboard/deleted.scss
+++ b/frontend/src/app/main/ui/dashboard/deleted.scss
@@ -6,11 +6,11 @@
@use "refactor/common-refactor.scss" as deprecated;
@use "common/refactor/common-dashboard";
-@use "../ds/typography.scss" as t;
-@use "../ds/_borders.scss" as *;
-@use "../ds/spacing.scss" as *;
-@use "../ds/_sizes.scss" as *;
-@use "../ds/z-index.scss" as *;
+@use "ds/typography.scss" as t;
+@use "ds/spacing.scss" as *;
+@use "ds/z-index.scss" as *;
+@use "ds/_borders.scss" as *;
+@use "ds/_sizes.scss" as *;
.dashboard-container {
flex: 1 0 0;
@@ -51,7 +51,7 @@
padding: var(--sp-xxl) var(--sp-xxl) var(--sp-s) var(--sp-xxl);
position: sticky;
top: 0;
- z-index: $z-index-100;
+ z-index: var(--z-index-panels);
}
.nav-inside {
diff --git a/frontend/src/app/main/ui/dashboard/files.scss b/frontend/src/app/main/ui/dashboard/files.scss
index 79f3563168..838f8ea78c 100644
--- a/frontend/src/app/main/ui/dashboard/files.scss
+++ b/frontend/src/app/main/ui/dashboard/files.scss
@@ -6,6 +6,8 @@
@use "refactor/common-refactor.scss" as deprecated;
@use "common/refactor/common-dashboard";
+@use "ds/_sizes.scss" as *;
+@use "ds/_utils.scss" as *;
.dashboard-container {
flex: 1 0 0;
@@ -13,6 +15,7 @@
overflow-y: auto;
width: 100%;
border-top: deprecated.$s-1 solid var(--color-background-quaternary);
+ padding-block-end: var(--sp-xxxl);
&.dashboard-projects {
user-select: none;
diff --git a/frontend/src/app/main/ui/dashboard/grid.scss b/frontend/src/app/main/ui/dashboard/grid.scss
index 3f4189c729..e1aaef396a 100644
--- a/frontend/src/app/main/ui/dashboard/grid.scss
+++ b/frontend/src/app/main/ui/dashboard/grid.scss
@@ -17,7 +17,7 @@ $thumbnail-default-height: deprecated.$s-168; // Default width
height: 100%;
overflow-y: auto;
overflow-x: hidden;
- padding: 0 deprecated.$s-16;
+ padding: 0 var(--sp-l) deprecated.$s-16;
}
.grid-row {
diff --git a/frontend/src/app/main/ui/dashboard/projects.scss b/frontend/src/app/main/ui/dashboard/projects.scss
index 7df6a0f9c9..a37575c38e 100644
--- a/frontend/src/app/main/ui/dashboard/projects.scss
+++ b/frontend/src/app/main/ui/dashboard/projects.scss
@@ -19,16 +19,15 @@
margin-inline-end: var(--sp-l);
border-block-start: $b-1 solid var(--panel-border-color);
overflow-y: auto;
- padding-block-end: var(--sp-xxxl);
}
.dashboard-projects {
user-select: none;
- block-size: calc(100vh - px2rem(64));
+ block-size: calc(100vh - px2rem(80));
}
.with-team-hero {
- block-size: calc(100vh - px2rem(280));
+ block-size: calc(100vh - px2rem(360));
}
.dashboard-shared {
diff --git a/frontend/src/app/main/ui/dashboard/templates.scss b/frontend/src/app/main/ui/dashboard/templates.scss
index 8a5ab660c0..5d58ac4fea 100644
--- a/frontend/src/app/main/ui/dashboard/templates.scss
+++ b/frontend/src/app/main/ui/dashboard/templates.scss
@@ -4,10 +4,11 @@
//
// Copyright (c) KALEIDOS INC
-@use "ds/_borders.scss" as *;
-@use "ds/_utils.scss" as *;
-@use "ds/_sizes.scss" as *;
@use "ds/typography.scss" as t;
+@use "ds/z-index.scss" as *;
+@use "ds/_borders.scss" as *;
+@use "ds/_sizes.scss" as *;
+@use "ds/_utils.scss" as *;
.dashboard-templates-section {
background-color: var(--color-background-tertiary);
@@ -26,6 +27,8 @@
transition: bottom 300ms;
width: calc(100% - $sz-12);
pointer-events: none;
+ z-index: var(--z-index-set);
+
&.collapsed {
inset-block-end: calc(-1 * px2rem(228));
background-color: transparent;
diff --git a/frontend/src/app/main/ui/settings/integrations.cljs b/frontend/src/app/main/ui/settings/integrations.cljs
index 780f9918e3..ff4b39049e 100644
--- a/frontend/src/app/main/ui/settings/integrations.cljs
+++ b/frontend/src/app/main/ui/settings/integrations.cljs
@@ -34,15 +34,8 @@
[app.util.dom :as dom]
[app.util.forms :as fm]
[app.util.i18n :as i18n :refer [tr]]
- [okulary.core :as l]
[rumext.v2 :as mf]))
-(def tokens-ref
- (l/derived :access-tokens st/state))
-
-(def token-created-ref
- (l/derived :access-token-created st/state))
-
(def notification-timeout 7000)
(def ^:private schema:form-access-token
@@ -78,7 +71,7 @@
(mf/defc token-created*
{::mf/private true}
[{:keys [title mcp-key?]}]
- (let [token-created (mf/deref token-created-ref)
+ (let [token-created (mf/deref refs/access-token-created)
on-copy-to-clipboard
(mf/use-fn
@@ -310,7 +303,7 @@
[]
(let [created? (mf/use-state false)
- tokens (mf/deref tokens-ref)
+ tokens (mf/deref refs/access-tokens)
mcp-key (some #(when (= (:type %) "mcp") %) tokens)
mcp-key-id (:id mcp-key)
@@ -413,7 +406,7 @@
(mf/defc mcp-server-section*
{::mf/private true}
[]
- (let [tokens (mf/deref tokens-ref)
+ (let [tokens (mf/deref refs/access-tokens)
profile (mf/deref refs/profile)
mcp-key (some #(when (= (:type %) "mcp") %) tokens)
@@ -422,6 +415,8 @@
expires-at (:expires-at mcp-key)
expired? (and (some? expires-at) (> (ct/now) expires-at))
+ show-enabled? (and mcp-enabled? (false? expired?))
+
tooltip-id
(mf/use-id)
@@ -511,14 +506,17 @@
(tr "integrations.mcp-server.status.expired.1")]]])
[:div {:class (stl/css :mcp-server-switch)}
- [:> switch* {:label (if mcp-enabled?
+ [:> switch* {:label (if show-enabled?
(tr "integrations.mcp-server.status.enabled")
(tr "integrations.mcp-server.status.disabled"))
- :default-checked mcp-enabled?
+ :default-checked show-enabled?
:on-change handle-mcp-change}]
(when (and (false? mcp-enabled?) (nil? mcp-key))
[:div {:class (stl/css :mcp-server-switch-cover)
- :on-click handle-generate-mcp-key}])]]]
+ :on-click handle-generate-mcp-key}])
+ (when (true? expired?)
+ [:div {:class (stl/css :mcp-server-switch-cover)
+ :on-click handle-regenerate-mcp-key}])]]]
(when (some? mcp-key)
[:div {:class (stl/css :mcp-server-key)}
@@ -567,7 +565,7 @@
(mf/defc access-tokens-section*
{::mf/private true}
[]
- (let [tokens (mf/deref tokens-ref)
+ (let [tokens (mf/deref refs/access-tokens)
handle-click
(mf/use-fn
diff --git a/frontend/src/app/main/ui/workspace/main_menu.cljs b/frontend/src/app/main/ui/workspace/main_menu.cljs
index 4d16c79646..674a01549d 100644
--- a/frontend/src/app/main/ui/workspace/main_menu.cljs
+++ b/frontend/src/app/main/ui/workspace/main_menu.cljs
@@ -43,13 +43,9 @@
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[beicon.v2.core :as rx]
- [okulary.core :as l]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
-(def tokens-ref
- (l/derived :access-tokens st/state))
-
(mf/defc shortcuts*
{::mf/private true}
[{:keys [id]}]
@@ -749,14 +745,22 @@
(mf/defc mcp-menu*
{::mf/private true}
[{:keys [on-close]}]
- (let [plugins? (features/active-feature? @st/state "plugins/runtime")
-
- profile (mf/deref refs/profile)
- mcp (mf/deref refs/mcp)
+ (let [plugins? (features/active-feature? @st/state "plugins/runtime")
+
+ profile (mf/deref refs/profile)
+ mcp (mf/deref refs/mcp)
+ tokens (mf/deref refs/access-tokens)
+
+ expired? (some->> tokens
+ (some #(when (= (:type %) "mcp") %))
+ :expires-at
+ (> (ct/now)))
mcp-enabled? (true? (-> profile :props :mcp-enabled))
mcp-connected? (= "connected" (get mcp :connection-status))
+ show-enabled? (and mcp-enabled? (false? expired?))
+
on-nav-to-integrations
(mf/use-fn
(fn []
@@ -794,7 +798,7 @@
:pos-6 plugins?)
:on-close on-close}
- (when mcp-enabled?
+ (when show-enabled?
[:> dropdown-menu-item* {:id "mcp-menu-toggle-mcp-plugin"
:class (stl/css :base-menu-item :submenu-item)
:on-click on-toggle-mcp-plugin
@@ -809,7 +813,7 @@
:on-click on-nav-to-integrations
:on-key-down on-nav-to-integrations-key-down}
[:span {:class (stl/css :item-name)}
- (if mcp-enabled?
+ (if show-enabled?
(tr "workspace.header.menu.mcp.server.status.enabled")
(tr "workspace.header.menu.mcp.server.status.disabled"))]]]))
@@ -983,7 +987,7 @@
:class (stl/css :item-arrow)}]])
(when (contains? cf/flags :mcp)
- (let [tokens (mf/deref tokens-ref)
+ (let [tokens (mf/deref refs/access-tokens)
expired? (some->> tokens
(some #(when (= (:type %) "mcp") %))
:expires-at
diff --git a/frontend/src/app/util/worker.cljs b/frontend/src/app/util/worker.cljs
index b23bbbee92..8d87a76795 100644
--- a/frontend/src/app/util/worker.cljs
+++ b/frontend/src/app/util/worker.cljs
@@ -90,8 +90,8 @@
"Return a initialized webworker instance."
[path on-error]
(let [instance (js/Worker. path)
- bus (rx/subject)
- worker (Worker. instance (rx/to-observable bus))
+ bus (rx/subject)
+ worker (Worker. instance (rx/to-observable bus))
handle-message
(fn [event]
diff --git a/mcp/packages/server/data/api_types.yml b/mcp/packages/server/data/api_types.yml
index 901037c249..54b4100e4a 100644
--- a/mcp/packages/server/data/api_types.yml
+++ b/mcp/packages/server/data/api_types.yml
@@ -26,6 +26,7 @@ Penpot:
props?: { [key: string]: unknown },
): symbol;
off(listenerId: symbol): void;
+ version: string;
root: Shape | null;
currentFile: File | null;
currentPage: Page | null;
@@ -72,7 +73,7 @@ Penpot:
generateFontFaces(shapes: Shape[]): Promise;
openViewer(): void;
createPage(): Page;
- openPage(page: Page, newWindow?: boolean): void;
+ openPage(page: string | Page, newWindow?: boolean): void;
alignHorizontal(
shapes: Shape[],
direction: "center" | "left" | "right",
@@ -162,6 +163,12 @@ Penpot:
```
penpot.closePlugin();
```
+ version: |-
+ ```
+ readonly version: string
+ ```
+
+ Returns the current penpot version.
root: |-
```
readonly root: Shape | null
@@ -725,19 +732,19 @@ Penpot:
Returns Page
openPage: |-
```
- openPage(page: Page, newWindow?: boolean): void
+ openPage(page: string | Page, newWindow?: boolean): void
```
Changes the current open page to given page. Requires `content:read` permission.
Parameters
- * page: Page
+ * page: string | Page
- the page to open
+ the page to open (a Page object or a page UUID string)
* newWindow: boolean
- if true opens the page in a new window
+ if true opens the page in a new window, defaults to false
Returns void
@@ -4785,6 +4792,7 @@ Context:
```
interface Context {
+ version: string;
root: Shape | null;
currentFile: File | null;
currentPage: Page | null;
@@ -4837,7 +4845,7 @@ Context:
removeListener(listenerId: symbol): void;
openViewer(): void;
createPage(): Page;
- openPage(page: Page, newWindow?: boolean): void;
+ openPage(page: string | Page, newWindow?: boolean): void;
alignHorizontal(
shapes: Shape[],
direction: "center" | "left" | "right",
@@ -4854,6 +4862,12 @@ Context:
```
members:
Properties:
+ version: |-
+ ```
+ readonly version: string
+ ```
+
+ Returns the current penpot version.
root: |-
```
readonly root: Shape | null
@@ -5392,19 +5406,19 @@ Context:
Returns Page
openPage: |-
```
- openPage(page: Page, newWindow?: boolean): void
+ openPage(page: string | Page, newWindow?: boolean): void
```
Changes the current open page to given page. Requires `content:read` permission.
Parameters
- * page: Page
+ * page: string | Page
- the page to open
+ the page to open (a Page object or a page UUID string)
* newWindow: boolean
- if true opens the page in a new window
+ if true opens the page in a new window, defaults to false
Returns void
@@ -6845,7 +6859,7 @@ Export:
```
interface Export {
- type: "svg" | "png" | "jpeg" | "pdf";
+ type: "svg" | "png" | "jpeg" | "webp" | "pdf";
scale?: number;
suffix?: string;
skipChildren?: boolean;
@@ -6857,10 +6871,10 @@ Export:
Properties:
type: |-
```
- type: "svg" | "png" | "jpeg" | "pdf"
+ type: "svg" | "png" | "jpeg" | "webp" | "pdf"
```
- Type of the file to export. Can be one of the following values: png, jpeg, svg, pdf
+ Type of the file to export. Can be one of the following values: png, jpeg, webp, svg, pdf
scale: |-
```
scale?: number
@@ -7249,6 +7263,7 @@ Flags:
```
interface Flags {
naturalChildOrdering: boolean;
+ throwValidationErrors: boolean;
}
```
@@ -7264,6 +7279,14 @@ Flags:
Also, appendChild method will be append the children in the top-most position.
The insertchild method is changed acordingly to respect this ordering.
Defaults to false
+ throwValidationErrors: |-
+ ```
+ throwValidationErrors: boolean
+ ```
+
+ If `true` the validation errors will throw an exception instead of displaying an
+ error in the debugger console.
+ Defaults to false
FlexLayout:
overview: |-
Interface FlexLayout
diff --git a/package.json b/package.json
index fbb4c5d92f..d2d6a9f5a8 100644
--- a/package.json
+++ b/package.json
@@ -16,9 +16,9 @@
"fmt": "./scripts/fmt"
},
"devDependencies": {
- "@github/copilot": "^1.0.21",
- "@types/node": "^25.5.2",
+ "@github/copilot": "^1.0.35",
+ "@types/node": "^25.6.0",
"esbuild": "^0.28.0",
- "opencode-ai": "^1.14.19"
+ "opencode-ai": "^1.14.22"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 32b4fa3382..cf5a098662 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -9,17 +9,17 @@ importers:
.:
devDependencies:
'@github/copilot':
- specifier: ^1.0.21
- version: 1.0.21
+ specifier: ^1.0.35
+ version: 1.0.35
'@types/node':
- specifier: ^25.5.2
- version: 25.5.2
+ specifier: ^25.6.0
+ version: 25.6.0
esbuild:
specifier: ^0.28.0
version: 0.28.0
opencode-ai:
- specifier: ^1.14.19
- version: 1.14.19
+ specifier: ^1.14.22
+ version: 1.14.22
packages:
@@ -179,120 +179,120 @@ packages:
cpu: [x64]
os: [win32]
- '@github/copilot-darwin-arm64@1.0.21':
- resolution: {integrity: sha512-aB+s9ldTwcyCOYmzjcQ4SknV6g81z92T8aUJEJZBwOXOTBeWKAJtk16ooAKangZgdwuLgO3or1JUjx1FJAm5nQ==}
+ '@github/copilot-darwin-arm64@1.0.35':
+ resolution: {integrity: sha512-NNZE0TOz0HOlv7eqlh6EcQbNkhtnIHReBLieW6pfDUUTKkgsqbUu1MOitF8m+LUQk3ml1T0MQ5MOfad1HSa/MQ==}
cpu: [arm64]
os: [darwin]
hasBin: true
- '@github/copilot-darwin-x64@1.0.21':
- resolution: {integrity: sha512-aNad81DOGuGShmaiFNIxBUSZLwte0dXmDYkGfAF9WJIgY4qP4A8CPWFoNr8//gY+4CwaIf9V+f/OC6k2BdECbw==}
+ '@github/copilot-darwin-x64@1.0.35':
+ resolution: {integrity: sha512-XCv/mfdv0rnrtrNVOluio/N/kyCge0uG2hghvtlgO/+z6EjvzFygkpXXS1gVxiXhWc3lX232cTXQU3zklC/8Ng==}
cpu: [x64]
os: [darwin]
hasBin: true
- '@github/copilot-linux-arm64@1.0.21':
- resolution: {integrity: sha512-FL0NsCnHax4czHVv1S8iBqPLGZDhZ28N3+6nT29xWGhmjBWTkIofxLThKUPcyyMsfPTTxIlrdwWa8qQc5z2Q+g==}
+ '@github/copilot-linux-arm64@1.0.35':
+ resolution: {integrity: sha512-mbaadATfJPzmXq2SD1TWocIG/GobcYC6OvNFhCG8UXMsiXY5cevhszl5ujuayhPJBxS77Yj5uvIFjNQ1Kf5V8Q==}
cpu: [arm64]
os: [linux]
hasBin: true
- '@github/copilot-linux-x64@1.0.21':
- resolution: {integrity: sha512-S7pWVI16hesZtxYbIyfw+MHZpc5ESoGKUVr5Y+lZJNaM2340gJGPQzQwSpvKIRMLHRKI2hXLwciAnYeMFxE/Tg==}
+ '@github/copilot-linux-x64@1.0.35':
+ resolution: {integrity: sha512-NrZ0VjztdBbJ5qAmuUtuKsWkimOaqzjDV+ZGUv1FxSxoys40kiiakQ5WbnMFDzaIFaf47zDi++6ixgQzq7Jk5A==}
cpu: [x64]
os: [linux]
hasBin: true
- '@github/copilot-win32-arm64@1.0.21':
- resolution: {integrity: sha512-a9qc2Ku+XbyBkXCclbIvBbIVnECACTIWnPctmXWsQeSdeapGxgfHGux7y8hAFV5j6+nhCm6cnyEMS3rkZjAhdA==}
+ '@github/copilot-win32-arm64@1.0.35':
+ resolution: {integrity: sha512-KQN7Q7+oPyglmvUEiMp6SYWjl30VSu91T0dUpNHbUs/xRM3qgnCymLPPUyBZGWHog/FueUAsRkhisMHWQVnO+g==}
cpu: [arm64]
os: [win32]
hasBin: true
- '@github/copilot-win32-x64@1.0.21':
- resolution: {integrity: sha512-9klu+7NQ6tEyb8sibb0rsbimBivDrnNltZho10Bgbf1wh3o+erTjffXDjW9Zkyaw8lZA9Fz8bqhVkKntZq58Lg==}
+ '@github/copilot-win32-x64@1.0.35':
+ resolution: {integrity: sha512-J0XhXO2FmlFr8pGa970xEd4tr1rqFiZxoaPW5WvkJYZoZUHbBhFcGasp5/yEeJ71b3vI4PHm/mSZZebD3ALMKQ==}
cpu: [x64]
os: [win32]
hasBin: true
- '@github/copilot@1.0.21':
- resolution: {integrity: sha512-P+nORjNKAtl92jYCG6Qr1Rsw2JoyScgeQSkIR6O2WB37WS5JVdA4ax1WVualMbfuc9V58CPHX6fwyNpkI89FkQ==}
+ '@github/copilot@1.0.35':
+ resolution: {integrity: sha512-O1nUy8DXOTE+v86b/FTkyu09EMrDy+vj+2rhmUOcmsXGe0RE5ECyESsasUTUoHK/CSgAExFTziNxbubUoiMMfg==}
hasBin: true
- '@types/node@25.5.2':
- resolution: {integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==}
+ '@types/node@25.6.0':
+ resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==}
esbuild@0.28.0:
resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==}
engines: {node: '>=18'}
hasBin: true
- opencode-ai@1.14.19:
- resolution: {integrity: sha512-67h56qYcJivd2U9VK8LJvyMBCc3ZE3HcJ/qL4YtaidSnjEumy4SxO+HHlDISsLase7TUQ0w1nULOAibdZxGzbQ==}
+ opencode-ai@1.14.22:
+ resolution: {integrity: sha512-J+q1Ehlfg7SSXw2aIY8Mb47FHhPTN8IciKNt0/D+H/brO8RWLe67WjFzxhh/z9SSad9wPcCiLRGAc/iAn8W8wA==}
hasBin: true
- opencode-darwin-arm64@1.14.19:
- resolution: {integrity: sha512-gjJ97dTiBbCas3Y7K6KQMQm5/KNIBOM9+10KZHqNr2bQfn0N09O977ZkXoX6IVBBE2632Ahaa71d4pzLKN9ULw==}
+ opencode-darwin-arm64@1.14.22:
+ resolution: {integrity: sha512-h9FjzNoDRsuJD0EEg535P9ul5TyrWovwx591VmuG8fp9d4PoSrAN1O3Zi07GJjkrYyrB8g3c+x5whDqJCz+qog==}
cpu: [arm64]
os: [darwin]
- opencode-darwin-x64-baseline@1.14.19:
- resolution: {integrity: sha512-UuwLpa511h7qQ+rTmOUmsHch/N4NBQT64dzg+iLWnR5yoR/81inENpbwxiS7hXpVdzCUGo/YnxI1u6SIBoMlTQ==}
+ opencode-darwin-x64-baseline@1.14.22:
+ resolution: {integrity: sha512-GgfP0wSm9/I+j3shOxfeA++7yZpXS6Y1Vis258nEFoRS9Xfv3YlHom7c/8BR9rYqeUE/+rrijP7PrGWGl+IHBw==}
cpu: [x64]
os: [darwin]
- opencode-darwin-x64@1.14.19:
- resolution: {integrity: sha512-uksrjOtWI7Ob5JvjZBSsrKuy3JVF9d89oYZYfWS5m8ordNyv1Nob39MXJXizv85ozsXjSb0rqjpJurnJw8K+tQ==}
+ opencode-darwin-x64@1.14.22:
+ resolution: {integrity: sha512-cyKRo22sxDwu4ITOlENwXaqVM9kMGndwSaAd95gz1Rmz5NYMShUO/8eckrD2MhS2wm+QvKw9XkRVWVHWQlZw3Q==}
cpu: [x64]
os: [darwin]
- opencode-linux-arm64-musl@1.14.19:
- resolution: {integrity: sha512-R1BJuBGWHfBxfKvIA/Hb4nhYaJgCKl1B+mAGNydu+z0CLtGtwU8r+kQWF/G2N0y8Vx6Y6DRfJiv1X0eZEfD1BQ==}
+ opencode-linux-arm64-musl@1.14.22:
+ resolution: {integrity: sha512-DtSd5tbGk6R5+hGhqViSvbY8ICf+u4oVQhfvCAplQCb1UEwYVc0+oAF6PimFJ+o8i8L6x14O0rry0NaRzZ0CzA==}
cpu: [arm64]
os: [linux]
- opencode-linux-arm64@1.14.19:
- resolution: {integrity: sha512-Bo+aZOppLF366mgGfK0CnIcAVy1EmsrBv93eot1CmPSN1oeud07GpGdn3Bjl5f6KuBx1io6JsvjQyHno+MH5AA==}
+ opencode-linux-arm64@1.14.22:
+ resolution: {integrity: sha512-ohK4LkkGvzB4ptr0nqDOVi2JEJMLROfy1s2U2A4Qrh+1Y0QimgH2b5VgTm+BjA3bC2Hm8Yf/IfkitqlUnCp7YA==}
cpu: [arm64]
os: [linux]
- opencode-linux-x64-baseline-musl@1.14.19:
- resolution: {integrity: sha512-+K8MuGoHugtUec4P/nKcTwZFUipHfW7oPpwlIoPiAQou3bNFTzzP6rslbzzNwjXlQRsUw9GAtuIPDOCL6CkgDg==}
+ opencode-linux-x64-baseline-musl@1.14.22:
+ resolution: {integrity: sha512-oZffotEbGXbA38Y0Dmj7IVq0ATl3nKbP8j91Z0zR5kBEBykOqExJIyc9pZpModgfPf86k98XBsRHiVLK4u9ARw==}
cpu: [x64]
os: [linux]
- opencode-linux-x64-baseline@1.14.19:
- resolution: {integrity: sha512-KuvITzg4iK0hdIjpNZepwu3bLZ/dUZDI6BwCoV4w//VEP1j3UfDyeS3vWghKcQLd2T1+yybuEMM/3RXcwm/lGQ==}
+ opencode-linux-x64-baseline@1.14.22:
+ resolution: {integrity: sha512-J67YAIWr3E03o9e6wNaPEqBo+9FcPKf5CzjIUSb8yNDyobWON1HHihcuu0hCJ6wF9J9awmlp2/4mO1HOoCo3QQ==}
cpu: [x64]
os: [linux]
- opencode-linux-x64-musl@1.14.19:
- resolution: {integrity: sha512-0qe2+X76UJdrCdhdlJyfubMC4tveHAVxjSmPq7g9Zm95heBeJdcQDCLeyQk/lGgeXgsZzVPfLmyTWNtBvCZYFQ==}
+ opencode-linux-x64-musl@1.14.22:
+ resolution: {integrity: sha512-r+QnqwR/OPmMm197Kb8VLD9mkZGFXz4m5QCZFxOAL34k8AhQZqn3d2mx2bfrMBVfoSiSVxa3jEjZEbNNFGlICQ==}
cpu: [x64]
os: [linux]
- opencode-linux-x64@1.14.19:
- resolution: {integrity: sha512-2GljfL7BeG4xALBJVRwaAGCM/dzYF5aQf6bfLTKsQIl6QpLUguYSF+fkStBHLeehyqbDP5MtiEEuXjC0+mecjA==}
+ opencode-linux-x64@1.14.22:
+ resolution: {integrity: sha512-MSUaO/Cvfb8DFRYETVrVeCnKtoIfgLflyB+O8xQOkVtjMKJ41M+1dFSMyZ3LQa2Vfp5tDskyMhj7eUxvT/owgQ==}
cpu: [x64]
os: [linux]
- opencode-windows-arm64@1.14.19:
- resolution: {integrity: sha512-8/vRHe5tHexikfPceLmpjsQiEhuDTOSCSlEmP4s0Yq3UAkVaDAxpiWq7Bx4g8hjr1gzfXD9vbjV+WHq/BtMC/w==}
+ opencode-windows-arm64@1.14.22:
+ resolution: {integrity: sha512-8grcxLSf9BD9Bt38MIxXfkI6aOFophVgM0US5r8nAUdVU78/8TS9Flnn6D39GM5RmxzqGWMl1u10vMFrBtMwPA==}
cpu: [arm64]
os: [win32]
- opencode-windows-x64-baseline@1.14.19:
- resolution: {integrity: sha512-Z8imEJK/srE/r1fr7oNLvpLTeRJQyuL7vsbXvCt3T7j2Ew9BOZ7RuYa8EE0R6bNqQ+MLhBGPiAG7NWc++MgK8Q==}
+ opencode-windows-x64-baseline@1.14.22:
+ resolution: {integrity: sha512-R/o36LpmQmbv/tL2pkcmApn6030z/1oJIYmjDkW5a4K5MXmV7aq+jWrH5p6iYKp9fo9L8oCtOp/rELMBqDS3UA==}
cpu: [x64]
os: [win32]
- opencode-windows-x64@1.14.19:
- resolution: {integrity: sha512-/TqGN91WiUzx7IPMPwmpMIzRixi5TMjaBcC9FH1TgD7DCqKnP6pokvu+ak0C9xwA4wKorE9PoZzeRt/+c3rDCQ==}
+ opencode-windows-x64@1.14.22:
+ resolution: {integrity: sha512-jVbZ4VA5b5MF2QhWQOE1VYBKdBE0v/ZebFjwzs6Vieazfgr6OFnGSHVP5WJbU/r6zDssbTBzzpnFxo0IY1SQWw==}
cpu: [x64]
os: [win32]
- undici-types@7.18.2:
- resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
+ undici-types@7.19.2:
+ resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==}
snapshots:
@@ -374,36 +374,36 @@ snapshots:
'@esbuild/win32-x64@0.28.0':
optional: true
- '@github/copilot-darwin-arm64@1.0.21':
+ '@github/copilot-darwin-arm64@1.0.35':
optional: true
- '@github/copilot-darwin-x64@1.0.21':
+ '@github/copilot-darwin-x64@1.0.35':
optional: true
- '@github/copilot-linux-arm64@1.0.21':
+ '@github/copilot-linux-arm64@1.0.35':
optional: true
- '@github/copilot-linux-x64@1.0.21':
+ '@github/copilot-linux-x64@1.0.35':
optional: true
- '@github/copilot-win32-arm64@1.0.21':
+ '@github/copilot-win32-arm64@1.0.35':
optional: true
- '@github/copilot-win32-x64@1.0.21':
+ '@github/copilot-win32-x64@1.0.35':
optional: true
- '@github/copilot@1.0.21':
+ '@github/copilot@1.0.35':
optionalDependencies:
- '@github/copilot-darwin-arm64': 1.0.21
- '@github/copilot-darwin-x64': 1.0.21
- '@github/copilot-linux-arm64': 1.0.21
- '@github/copilot-linux-x64': 1.0.21
- '@github/copilot-win32-arm64': 1.0.21
- '@github/copilot-win32-x64': 1.0.21
+ '@github/copilot-darwin-arm64': 1.0.35
+ '@github/copilot-darwin-x64': 1.0.35
+ '@github/copilot-linux-arm64': 1.0.35
+ '@github/copilot-linux-x64': 1.0.35
+ '@github/copilot-win32-arm64': 1.0.35
+ '@github/copilot-win32-x64': 1.0.35
- '@types/node@25.5.2':
+ '@types/node@25.6.0':
dependencies:
- undici-types: 7.18.2
+ undici-types: 7.19.2
esbuild@0.28.0:
optionalDependencies:
@@ -434,55 +434,55 @@ snapshots:
'@esbuild/win32-ia32': 0.28.0
'@esbuild/win32-x64': 0.28.0
- opencode-ai@1.14.19:
+ opencode-ai@1.14.22:
optionalDependencies:
- opencode-darwin-arm64: 1.14.19
- opencode-darwin-x64: 1.14.19
- opencode-darwin-x64-baseline: 1.14.19
- opencode-linux-arm64: 1.14.19
- opencode-linux-arm64-musl: 1.14.19
- opencode-linux-x64: 1.14.19
- opencode-linux-x64-baseline: 1.14.19
- opencode-linux-x64-baseline-musl: 1.14.19
- opencode-linux-x64-musl: 1.14.19
- opencode-windows-arm64: 1.14.19
- opencode-windows-x64: 1.14.19
- opencode-windows-x64-baseline: 1.14.19
+ opencode-darwin-arm64: 1.14.22
+ opencode-darwin-x64: 1.14.22
+ opencode-darwin-x64-baseline: 1.14.22
+ opencode-linux-arm64: 1.14.22
+ opencode-linux-arm64-musl: 1.14.22
+ opencode-linux-x64: 1.14.22
+ opencode-linux-x64-baseline: 1.14.22
+ opencode-linux-x64-baseline-musl: 1.14.22
+ opencode-linux-x64-musl: 1.14.22
+ opencode-windows-arm64: 1.14.22
+ opencode-windows-x64: 1.14.22
+ opencode-windows-x64-baseline: 1.14.22
- opencode-darwin-arm64@1.14.19:
+ opencode-darwin-arm64@1.14.22:
optional: true
- opencode-darwin-x64-baseline@1.14.19:
+ opencode-darwin-x64-baseline@1.14.22:
optional: true
- opencode-darwin-x64@1.14.19:
+ opencode-darwin-x64@1.14.22:
optional: true
- opencode-linux-arm64-musl@1.14.19:
+ opencode-linux-arm64-musl@1.14.22:
optional: true
- opencode-linux-arm64@1.14.19:
+ opencode-linux-arm64@1.14.22:
optional: true
- opencode-linux-x64-baseline-musl@1.14.19:
+ opencode-linux-x64-baseline-musl@1.14.22:
optional: true
- opencode-linux-x64-baseline@1.14.19:
+ opencode-linux-x64-baseline@1.14.22:
optional: true
- opencode-linux-x64-musl@1.14.19:
+ opencode-linux-x64-musl@1.14.22:
optional: true
- opencode-linux-x64@1.14.19:
+ opencode-linux-x64@1.14.22:
optional: true
- opencode-windows-arm64@1.14.19:
+ opencode-windows-arm64@1.14.22:
optional: true
- opencode-windows-x64-baseline@1.14.19:
+ opencode-windows-x64-baseline@1.14.22:
optional: true
- opencode-windows-x64@1.14.19:
+ opencode-windows-x64@1.14.22:
optional: true
- undici-types@7.18.2: {}
+ undici-types@7.19.2: {}