diff --git a/docs/css/index.css b/docs/css/index.css index 63282f168a..9737456609 100644 --- a/docs/css/index.css +++ b/docs/css/index.css @@ -848,6 +848,9 @@ a[href].post-tag:visited { .illus-techguide { background-image: url(/img/home-technical-guide.webp); } +.illus-mcp { + background-image: url(/img/home-mcp-server.webp); +} .illus-plugins { background-image: url(/img/home-plugins.webp); } diff --git a/docs/index.njk b/docs/index.njk index ef15854588..908d2f00b0 100644 --- a/docs/index.njk +++ b/docs/index.njk @@ -18,24 +18,30 @@ eleventyNavigation:

Everything you need to know about how Penpot works.

-
  • - -

    Contributing guide →

    -

    How to report bugs, add translations and more.

    -
    -
  • Technical guide →

    Installation, configuration and architecture.

  • +
  • + +

    MCP server →

    +

    Connect AI agents to your Penpot files for design and development workflows.

    +
    +
  • Plugins →

    All about Penpot plugins.

  • +
  • + +

    Contributing guide →

    +

    How to report bugs, add translations and more.

    +
    +
  • FAQs →

    diff --git a/docs/mcp/index.md b/docs/mcp/index.md index e0d31fe6ce..466144b3f1 100644 --- a/docs/mcp/index.md +++ b/docs/mcp/index.md @@ -309,6 +309,38 @@ For client-specific setup, use the shared section **Connect your MCP client**. For remote mode, use the URL shown in **Your account → Integrations → MCP Server**, which includes your `userToken`. +### Setup videos + +
    + +
    Penpot MCP Server – Remote setup with OpenCode and OpenRouter
    +
    + +
    + +
    Penpot MCP Server – Remote setup with Cursor
    +
    +
    ### Use @@ -391,6 +423,23 @@ Leave this terminal running while you use MCP. For advanced or repository-based workflows, see the [MCP README](https://github.com/penpot/penpot/blob/main/mcp/README.md) in the Penpot repository. +### Setup video + +
    + +
    Penpot MCP Server – Local setup
    +
    + ### Connect diff --git a/exporter/scripts/build b/exporter/scripts/build index 3daa2cf6f5..40eba8f44c 100755 --- a/exporter/scripts/build +++ b/exporter/scripts/build @@ -14,6 +14,7 @@ rm -rf target pnpm run build; cp pnpm-lock.yaml target/; +cp pnpm-workspace.yaml target/; cp package.json target/; touch target/pnpm-workspace.yaml; diff --git a/frontend/src/app/main/ui/onboarding/questions.cljs b/frontend/src/app/main/ui/onboarding/questions.cljs index d3974ed2f2..64fff7896e 100644 --- a/frontend/src/app/main/ui/onboarding/questions.cljs +++ b/frontend/src/app/main/ui/onboarding/questions.cljs @@ -208,53 +208,28 @@ [:and [:map {:title "QuestionsFormStep3"} [:team-size - [:enum "more-than-50" "31-50" "11-30" "2-10" "freelancer" "personal-project"]] - - [:planning ::sm/text] - - [:planning-other {:optional true} - [::sm/text {:max 512}]]] - - [:fn {:error/field :planning-other} - (fn [{:keys [planning planning-other]}] - (or (not= planning "other") - (and (= planning "other") - (not (str/blank? planning-other)))))]]) + [:enum "1" "2-100" "101-500" "501-1000" "1001-5000" "5001+"]]]]) (mf/defc step-3 {::mf/props :obj} [{:keys [on-next on-prev form show-step-3]}] (let [team-size-options (mf/with-memo [] - [{:label (tr "labels.select-option") :value "" :key "team-size" :disabled true} - {:label (tr "onboarding.questions.team-size.more-than-50") :value "more-than-50" :key "more-than-50"} - {:label (tr "onboarding.questions.team-size.31-50") :value "31-50" :key "31-50"} - {:label (tr "onboarding.questions.team-size.11-30") :value "11-30" :key "11-30"} - {:label (tr "onboarding.questions.team-size.2-10") :value "2-10" :key "2-10"} - {:label (tr "onboarding.questions.team-size.freelancer") :value "freelancer" :key "freelancer"} - {:label (tr "onboarding.questions.team-size.personal-project") :value "personal-project" :key "personal-project"}]) - - planning-options - (mf/with-memo [] - (-> (shuffle [{:label (tr "labels.select-option") - :value "" :key "questions:what-brings-you-here" - :disabled true} - {:label (tr "onboarding.questions.reasons.exploring") - :value "discover-more-about-penpot" - :key "discover-more-about-penpot"} - {:label (tr "onboarding.questions.reasons.fit") - :value "test-penpot-to-see-if-its-a-fit-for-team" - :key "test-penpot-to-see-if-its-a-fit-for-team"} - {:label (tr "onboarding.questions.reasons.alternative") - :value "alternative-to-figma" - :key "alternative-to-figma"} - {:label (tr "onboarding.questions.reasons.testing") - :value "try-out-before-using-penpot-on-premise" - :key "try-out-before-using-penpot-on-premise"}]) - (conj {:label (tr "labels.other-short") :value "other"}))) - - current-planning - (dm/get-in @form [:data :planning])] + [{:label (tr "labels.select-option") + :value "" :key "team-size" + :disabled true} + {:label (tr "onboarding.questions.team-size.just-me") + :value "1" :key "1"} + {:label (tr "onboarding.questions.team-size.2-100") + :value "2-100" :key "2-100"} + {:label (tr "onboarding.questions.team-size.101-500") + :value "101-500" :key "101-500"} + {:label (tr "onboarding.questions.team-size.501-1000") + :value "501-1000" :key "501-1000"} + {:label (tr "onboarding.questions.team-size.1001-5000") + :value "1001-5000" :key "1001-5000"} + {:label (tr "onboarding.questions.team-size.more-than-5001") + :value "5001+" :key "5001+"}])] [:& step-container {:form form :step 3 @@ -267,26 +242,8 @@ [:h1 {:class (stl/css :modal-title)} (tr "onboarding.questions.step3.title")] - [:div {:class (stl/css :modal-question)} - [:h3 {:class (stl/css :modal-subtitle)} - (tr "onboarding.questions.step1.question2")] - - [:& fm/select - {:options planning-options - :select-class (stl/css :select-class) - :default "" - :name :planning - :dropdown-class (stl/css :question-dropdown)}]] - - (when (= current-planning "other") - [:& fm/input {:name :planning-other - :class (stl/css :input-spacing) - :placeholder (tr "labels.other") - :show-error false - :label ""}]) [:div {:class (stl/css :modal-question)} - [:h3 {:class (stl/css :modal-subtitle)} (tr "onboarding.questions.step3.question3")] [:& fm/select {:options team-size-options :default "" :select-class (stl/css :select-class) diff --git a/frontend/src/app/main/ui/onboarding/questions.scss b/frontend/src/app/main/ui/onboarding/questions.scss index 9265788c98..4438dcc1fb 100644 --- a/frontend/src/app/main/ui/onboarding/questions.scss +++ b/frontend/src/app/main/ui/onboarding/questions.scss @@ -119,7 +119,7 @@ // STEP-3 .step-3 { - grid-template-rows: deprecated.$s-20 auto auto auto auto deprecated.$s-32; + grid-template-rows: deprecated.$s-20 auto auto deprecated.$s-32; } .image-radio { diff --git a/frontend/src/app/main/ui/workspace/main_menu.cljs b/frontend/src/app/main/ui/workspace/main_menu.cljs index d4d0dd9f28..20a50c729a 100644 --- a/frontend/src/app/main/ui/workspace/main_menu.cljs +++ b/frontend/src/app/main/ui/workspace/main_menu.cljs @@ -30,6 +30,7 @@ [app.main.data.workspace.versions :as dwv] [app.main.features :as features] [app.main.refs :as refs] + [app.main.repo :as rp] [app.main.store :as st] [app.main.ui.components.dropdown-menu :refer [dropdown-menu* dropdown-menu-item*]] @@ -938,15 +939,29 @@ (fn [event] (dom/stop-propagation event) (let [renderer (or (-> profile :props :renderer) :svg) - next-renderer (if (= renderer :wasm) :svg :wasm)] - (st/emit! (ev/event {::ev/name (if (= next-renderer :wasm) - "enable-webgl-rendering" - "disable-webgl-rendering") - ::ev/origin "workspace:menu"}) - (du/update-profile-props {:renderer next-renderer}) - (ntf/success (tr (if (= next-renderer :wasm) - "webgl.toast.webgl-render-enabled" - "webgl.toast.webgl-render-disabled"))))))) + next-renderer (if (= renderer :wasm) :svg :wasm) + ev-name (if (= next-renderer :wasm) + "enable-webgl-rendering" + "disable-webgl-rendering")] + (if (cf/external-feature-flag "renderer-hard-reload" "test") + ;; Bare RPC + hard reload: skips `du/update-profile-props`, so + ;; `features/recompute-features` is not run here; bootstrap + ;; after reload resolves render-wasm/v1 from the saved profile. + (do + (st/emit! (ev/event {::ev/name ev-name + ::ev/origin "workspace:menu"})) + (->> (rp/cmd! :update-profile-props {:props {:renderer next-renderer}}) + (rx/subs! (fn [_] (dom/reload-current-window true)) + (fn [_] + (st/emit! (ntf/error (tr "errors.generic"))))))) + ;; `update-profile-props` WatchEvent calls + ;; `features/recompute-features`. + (st/emit! (ev/event {::ev/name ev-name + ::ev/origin "workspace:menu"}) + (du/update-profile-props {:renderer next-renderer}) + (ntf/success (tr (if (= next-renderer :wasm) + "webgl.toast.webgl-render-enabled" + "webgl.toast.webgl-render-disabled")))))))) open-plugins-manager (mf/use-fn diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 64dc9c4a41..ad7034181f 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -4873,7 +4873,7 @@ msgstr "What's the size of your company?" #: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.step3.title" -msgstr "Tell us about your job" +msgstr "What is your company size?" #: src/app/main/ui/onboarding/questions.cljs:344 msgid "onboarding.questions.step4.title" @@ -4884,7 +4884,25 @@ msgstr "Where would you like to get started?" msgid "onboarding.questions.step5.title" msgstr "How did you hear about Penpot?" -#: src/app/main/ui/onboarding/questions.cljs:232 +msgid "onboarding.questions.team-size.just-me" +msgstr "Just me" + +msgid "onboarding.questions.team-size.2-100" +msgstr "2-100" + +msgid "onboarding.questions.team-size.101-500" +msgstr "101-500" + +msgid "onboarding.questions.team-size.501-1000" +msgstr "501-1,000" + +msgid "onboarding.questions.team-size.1001-5000" +msgstr "1,001-5,000" + +msgid "onboarding.questions.team-size.more-than-5001" +msgstr "5,001+" + +#: src/app/main/ui/onboarding/questions.cljs:233 msgid "onboarding.questions.team-size.11-30" msgstr "11-30" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 9050f78a21..c2a31d5670 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -4742,7 +4742,7 @@ msgstr "¿Cuál es el tamaño de tu empresa?" #: src/app/main/ui/onboarding/questions.cljs:269 msgid "onboarding.questions.step3.title" -msgstr "Háblanos de tu trabajo" +msgstr "¿Cuál es el tamaño de tu empresa?" #: src/app/main/ui/onboarding/questions.cljs:344 msgid "onboarding.questions.step4.title" @@ -4753,7 +4753,25 @@ msgstr "¿Por dónde te apetecería empezar?" msgid "onboarding.questions.step5.title" msgstr "¿Cómo nos has descubierto?" -#: src/app/main/ui/onboarding/questions.cljs:232 +msgid "onboarding.questions.team-size.just-me" +msgstr "Sólo yo" + +msgid "onboarding.questions.team-size.2-100" +msgstr "2-100" + +msgid "onboarding.questions.team-size.101-500" +msgstr "101-500" + +msgid "onboarding.questions.team-size.501-1000" +msgstr "501-1000" + +msgid "onboarding.questions.team-size.1001-5000" +msgstr "1001-5000" + +msgid "onboarding.questions.team-size.more-than-5001" +msgstr "5001+" + +#: src/app/main/ui/onboarding/questions.cljs:233 msgid "onboarding.questions.team-size.11-30" msgstr "11-30"