From 39e2ca08516066066cb382cbacd37a2574059993 Mon Sep 17 00:00:00 2001 From: Holly Schinsky Date: Thu, 20 Nov 2025 23:34:59 -0500 Subject: [PATCH 01/11] Updated upload script and implements launch playground button in header --- .../components/GlobalHeader/avatar.svg | 1 + .../components/GlobalHeader/index.js | 1109 +++++++++++++++++ .../components/Layout/index.js | 16 + .../guides/getting_started/hello-world.md | 8 + .../guides/learn/how_to/position_elements.md | 2 +- src/pages/guides/learn/how_to/use_geometry.md | 4 +- upload-playground-samples.mjs | 53 +- 7 files changed, 1186 insertions(+), 7 deletions(-) create mode 100644 src/@adobe/gatsby-theme-aio/components/GlobalHeader/avatar.svg create mode 100644 src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js create mode 100644 src/@adobe/gatsby-theme-aio/components/Layout/index.js diff --git a/src/@adobe/gatsby-theme-aio/components/GlobalHeader/avatar.svg b/src/@adobe/gatsby-theme-aio/components/GlobalHeader/avatar.svg new file mode 100644 index 000000000..75305cc07 --- /dev/null +++ b/src/@adobe/gatsby-theme-aio/components/GlobalHeader/avatar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js b/src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js new file mode 100644 index 000000000..15d01eacd --- /dev/null +++ b/src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js @@ -0,0 +1,1109 @@ +/* + * Copyright 2020 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import React, { Fragment, useRef, useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import nextId from 'react-id-generator'; +import { withPrefix } from 'gatsby'; +import { GatsbyLink } from '@adobe/gatsby-theme-aio/src/components/GatsbyLink'; +import { + findSelectedTopPage, + findSelectedTopPageMenu, + rootFix, + rootFixPages, + getExternalLinkProps, + DESKTOP_SCREEN_WIDTH, + MOBILE_SCREEN_WIDTH, + DEFAULT_HOME, +} from '@adobe/gatsby-theme-aio/src/utils'; +import { css } from '@emotion/react'; +import { AnchorButton } from '@adobe/gatsby-theme-aio/src/components/AnchorButton'; +import { Button } from '@adobe/gatsby-theme-aio/src/components/Button'; +import { ProgressCircle } from '@adobe/gatsby-theme-aio/src/components/ProgressCircle'; +import { Adobe, ChevronDown, Magnify, Close, TripleGripper, CheckMark } from '@adobe/gatsby-theme-aio/src/components/Icons'; +import { ActionButton, Text as ActionButtonLabel } from '@adobe/gatsby-theme-aio/src/components/ActionButton'; +import { PickerButton } from '@adobe/gatsby-theme-aio/src/components/Picker'; +import { Menu, Item as MenuItem } from '@adobe/gatsby-theme-aio/src/components/Menu'; +import { Popover } from '@adobe/gatsby-theme-aio/src/components/Popover'; +import { Image } from '@adobe/gatsby-theme-aio/src/components/Image'; +import { Link } from '@adobe/gatsby-theme-aio/src/components/Link'; +import { + Tabs, + HeaderTabItem as TabsItem, + Label as TabsItemLabel, + TabsIndicator, + positionIndicator, + animateIndicator, +} from '@adobe/gatsby-theme-aio/src/components/Tabs'; +import '@spectrum-css/typography'; +import '@spectrum-css/assetlist'; +import { Divider } from '@adobe/gatsby-theme-aio/src/components/Divider'; +import DEFAULT_AVATAR from './avatar.svg'; + +const getSelectedTabIndex = (location, pages) => { + const pathWithRootFix = rootFix(location.pathname); + const pagesWithRootFix = rootFixPages(pages); + + let selectedIndex = pagesWithRootFix.indexOf( + findSelectedTopPage(pathWithRootFix, pagesWithRootFix) + ); + let tempArr = pathWithRootFix.split('/'); + let inx = tempArr.indexOf('use-cases'); + if (selectedIndex === -1 && inx > -1) { + tempArr[inx + 1] = 'agreements-and-contracts'; + tempArr[inx + 2] = 'sales-proposals-and-contracts'; + if (tempArr[inx + 3] == undefined) { + tempArr.push(''); + } + let tempPathName = tempArr.join('/'); + selectedIndex = pagesWithRootFix.indexOf(findSelectedTopPage(tempPathName, pagesWithRootFix)); + } + // Assume first item is selected + if (selectedIndex === -1) { + selectedIndex = 0; + } + return selectedIndex; +}; + +const getAvatar = async userId => { + try { + const req = await fetch(`https://cc-api-behance.adobe.io/v2/users/${userId}?api_key=SUSI2`); + const res = await req.json(); + return res?.user?.images?.['138'] ?? DEFAULT_AVATAR; + } catch (e) { + console.warn(e); + return DEFAULT_AVATAR; + } +}; + +const GlobalHeader = ({ + hasIMS, + ims, + isLoadingIms, + home, + versions, + pages, + docs, + location, + toggleSideNav, + hasSideNav, + hasSearch, + showSearch, + setShowSearch, + searchButtonId, +}) => { + const [selectedTabIndex, setSelectedTabIndex] = useState(getSelectedTabIndex(location, pages)); + const tabsRef = useRef(null); + const tabsContainerRef = useRef(null); + const selectedTabIndicatorRef = useRef(null); + // Don't animate the tab indicator by default + const [isAnimated, setIsAnimated] = useState(false); + const versionPopoverRef = useRef(null); + const profilePopoverRef = useRef(null); + const [openVersion, setOpenVersion] = useState(false); + const [openProfile, setOpenProfile] = useState(false); + const [openMenuIndex, setOpenMenuIndex] = useState(-1); + const [profile, setProfile] = useState(null); + const [avatar, setAvatar] = useState(DEFAULT_AVATAR); + const [isLoadingProfile, setIsLoadingProfile] = useState(true); + const [selectedMenuItem, setSelectedMenuItem] = useState({}); + + const POPOVER_ANIMATION_DELAY = 200; + const versionPopoverId = 'version ' + nextId(); + const profilePopoverId = 'profile ' + nextId(); + const hasHome = home?.hidden !== true; + + const positionSelectedTabIndicator = index => { + const selectedTab = pages[index].tabRef; + + if (selectedTab?.current) { + positionIndicator(selectedTabIndicatorRef, selectedTab); + } + }; + + useEffect(() => { + const index = getSelectedTabIndex(location, pages); + setSelectedTabIndex(index); + const pathWithRootFix = rootFix(location.pathname); + setSelectedMenuItem(findSelectedTopPageMenu(pathWithRootFix, pages[index])); + animateIndicator(selectedTabIndicatorRef, isAnimated); + positionSelectedTabIndicator(index); + }, [location.pathname]); + + useEffect(() => { + (async () => { + if (ims && ims.isSignedInUser()) { + const profile = await ims.getProfile(); + setProfile(profile); + setAvatar(await getAvatar(profile.userId)); + setIsLoadingProfile(false); + } else if (!isLoadingIms) { + setIsLoadingProfile(false); + } + })(); + }, [ims]); + + useEffect(() => { + if (versionPopoverRef.current) { + if (openVersion) { + const { left } = versionPopoverRef.current.getBoundingClientRect(); + + versionPopoverRef.current.style.left = `calc(${left}px + var(--spectrum-global-dimension-size-160))`; + versionPopoverRef.current.style.position = 'fixed'; + } else { + // Wait for animation to finish + setTimeout(() => { + versionPopoverRef.current.style = ''; + }, POPOVER_ANIMATION_DELAY); + } + } + }, [openVersion]); + + useEffect(() => { + if (openMenuIndex !== -1) { + const menuRef = pages[openMenuIndex].menuRef; + + const { left } = menuRef.current.getBoundingClientRect(); + + menuRef.current.style.left = `${left}px`; + menuRef.current.style.position = 'fixed'; + } else { + pages.forEach(page => { + const menuRef = page.menuRef; + if (menuRef) { + // Wait for animation to finish + setTimeout(() => { + menuRef.current.style = ''; + }, POPOVER_ANIMATION_DELAY); + } + }); + } + }, [openMenuIndex]); + + useEffect(() => { + // Clicking outside of menu should close menu + const onClick = event => { + if (versionPopoverRef.current && !versionPopoverRef.current.contains(event.target)) { + setOpenVersion(false); + } + + if (profilePopoverRef?.current && !profilePopoverRef.current.contains(event.target)) { + setOpenProfile(false); + } + + pages.some(page => { + if (page?.menuRef?.current && !page.menuRef.current.contains(event.target)) { + setOpenMenuIndex(-1); + } + }); + }; + + document.addEventListener('click', onClick); + + return () => document.removeEventListener('click', onClick); + }, []); + + useEffect(() => { + const onScroll = () => { + setOpenVersion(false); + setOpenMenuIndex(-1); + }; + + tabsContainerRef.current.addEventListener('scroll', onScroll, { passive: true }); + + return () => tabsContainerRef.current.removeEventListener('scroll', onScroll); + }, []); + + const openDropDown = data => { + if (data.isOpen) { + setOpenMenuIndex(data.index); + setOpenVersion(data.isOpen); + if (openMenuIndex === -1 || openMenuIndex !== data.index) { + setTimeout(() => { + document.getElementById(`menuIndex${data.index}-0`).focus(); + }, 100); + } + } + }; + + const handleCredential = () => { + + const section = document.getElementById('adobe-get-credential'); + + if (section) { + section.scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center', + }); + } + + } + + return ( +
+ +
+ ); +}; + +GlobalHeader.propTypes = { + ims: PropTypes.object, + isLoadingIms: PropTypes.bool, + home: PropTypes.object, + versions: PropTypes.array, + pages: PropTypes.array, + docs: PropTypes.object, + location: PropTypes.object, + toggleSideNav: PropTypes.func, + hasSideNav: PropTypes.bool, + setShowSearch: PropTypes.func, + hasSearch: PropTypes.bool, + showSearch: PropTypes.bool, + searchButtonId: PropTypes.string, +}; + +export { GlobalHeader }; diff --git a/src/@adobe/gatsby-theme-aio/components/Layout/index.js b/src/@adobe/gatsby-theme-aio/components/Layout/index.js new file mode 100644 index 000000000..b004cac71 --- /dev/null +++ b/src/@adobe/gatsby-theme-aio/components/Layout/index.js @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +// Re-export the original Layout component +// This allows the shadowed GlobalHeader to work properly +export { default } from '@adobe/gatsby-theme-aio/src/components/Layout'; + diff --git a/src/pages/guides/getting_started/hello-world.md b/src/pages/guides/getting_started/hello-world.md index b30d911f3..95249875a 100644 --- a/src/pages/guides/getting_started/hello-world.md +++ b/src/pages/guides/getting_started/hello-world.md @@ -36,6 +36,14 @@ This guide is **divided into two tracks**, which you can follow independently of
+
+ +
+ The [Code Playground](#code-playground) path is based on a browser sandbox that runs instantly, requires no installation, and lets you explore add-on APIs with real-time feedback directly inside Adobe Express. **If you are new to add-on development**, or prefer to tinker-to-learn, then begin in the Playground to familiarise yourself with the environment; you can always try the CLI later. The [Command Line Interface (CLI)](#command-line-interface-cli) path will teach you to set up a local development environment, complete with a build pipeline, that allows you to build more complex add-ons that include external dependencies. This is the preferred path **for developers who want fully control**. You can always prototype rapidly in the Playground and transition to the CLI whenever project complexity calls for it. diff --git a/src/pages/guides/learn/how_to/position_elements.md b/src/pages/guides/learn/how_to/position_elements.md index 2b5e360f8..e3d92b3e5 100644 --- a/src/pages/guides/learn/how_to/position_elements.md +++ b/src/pages/guides/learn/how_to/position_elements.md @@ -121,7 +121,7 @@ By definition, the bounds of an element (or its _bounding box_) are the smallest Let's see how to get the bounds of a rotated rectangle in both local and parent coordinates; since the rectangle is rotated, the two bounding boxes will differ. -```js +```js{try id=createAndRotateRectangle} // sandbox/code.js import { editor } from "express-document-sdk"; diff --git a/src/pages/guides/learn/how_to/use_geometry.md b/src/pages/guides/learn/how_to/use_geometry.md index e5c5a5d8b..2a27dd649 100644 --- a/src/pages/guides/learn/how_to/use_geometry.md +++ b/src/pages/guides/learn/how_to/use_geometry.md @@ -111,9 +111,9 @@ currentPage.artboards.first.children.append(rect); ### Example: Style Shapes -Shapes have `fill` and `stroke` properties that you can use to style them. The following example demonstrates how to create a rectangle with a fill and a stroke. +Shapes have `fill` and `stroke` properties that you can use to style them. The following example demonstrates how to create an ellipse with a fill and a stroke. -```js +```js{try id=createEllipseWithFillAndStroke} // sandbox/code.js import { editor, colorUtils, constants } from "express-document-sdk"; diff --git a/upload-playground-samples.mjs b/upload-playground-samples.mjs index 836155bcb..2b0c5cf5a 100644 --- a/upload-playground-samples.mjs +++ b/upload-playground-samples.mjs @@ -73,13 +73,40 @@ async function getImsServiceToken() { } } +/** + * Comment out express-document-sdk import statements in code. + * The Code Playground Script mode automatically imports these modules, + * so we comment them out to avoid conflicts while preserving them for educational context. + * @param code - The code to process. + * @returns the code with import statements commented out. + */ +function commentOutExpressDocumentSDKImports(code) { + // Comment out import statements for express-document-sdk + // Handles various import formats: + // - import { editor } from "express-document-sdk"; + // - import { editor, fonts } from "express-document-sdk"; + // - import * as expressSDK from "express-document-sdk"; + // - Single or double quotes + const importRegex = /^(import\s+.*\s+from\s+["']express-document-sdk["'];?\s*)$/gm; + + // Replace with commented version and add helpful note + const processedCode = code.replace( + importRegex, + "// Note: Uncomment the import below when using in your add-on's code.js\n// $1" + ); + + return processedCode; +} + /** * Create a zip file from a code block. * @param block - The code block to create a zip file from. */ async function createZipFileFromCodeBlock(block) { const zip = new JSZip(); - zip.file("script.js", block.code); + // Comment out express-document-sdk imports before adding to zip + const processedCode = commentOutExpressDocumentSDKImports(block.code); + zip.file("script.js", processedCode); return zip.generateAsync({ type: "nodebuffer" }); } @@ -91,13 +118,29 @@ async function createZipFileFromCodeBlock(block) { */ async function uploadCodeBlockToFFC(codeBlock, projectId) { try { + // Process the code and log it for verification + const processedCode = commentOutExpressDocumentSDKImports(codeBlock.code); + + console.log("\n" + "=".repeat(80)); + console.log(`Uploading code block: ${projectId}`); + console.log("File: " + codeBlock.filePath); + console.log("-".repeat(80)); + console.log("Processed code that will be uploaded:"); + console.log("-".repeat(80)); + console.log(processedCode); + console.log("=".repeat(80) + "\n"); + const accessToken = await getImsServiceToken(); const url = new URL( `${FFC_PLAYGROUND_ENDPOINT}/${projectId}`, FFC_BASE_URL ); - const zipBuffer = await createZipFileFromCodeBlock(codeBlock); + // Create zip with the already-processed code + const zip = new JSZip(); + zip.file("script.js", processedCode); + const zipBuffer = await zip.generateAsync({ type: "nodebuffer" }); + const form = new FormData(); form.append( "file", @@ -105,7 +148,7 @@ async function uploadCodeBlockToFFC(codeBlock, projectId) { `${projectId}.zip` ); form.append("name", projectId); - + const response = await fetch(url, { method: "PUT", headers: { @@ -124,9 +167,11 @@ async function uploadCodeBlockToFFC(codeBlock, projectId) { `Failed to upload code block to FFC - HTTP ${response.status}: ${text}` ); } + + console.log(`āœ… Successfully uploaded: ${projectId}\n`); return response.json(); } catch (error) { - console.error("Failed to upload code block to FFC:", error.message); + console.error(`āŒ Failed to upload code block to FFC (${projectId}):`, error.message); throw error; } } From 7508709a1d4ba7962ff3e3258b5011ff204406b6 Mon Sep 17 00:00:00 2001 From: Holly Schinsky Date: Fri, 21 Nov 2025 00:14:59 -0500 Subject: [PATCH 02/11] More deep-links with Try button --- .../guides/learn/how_to/group_elements.md | 6 +++--- .../guides/learn/how_to/position_elements.md | 5 ++++- src/pages/guides/learn/how_to/use_color.md | 8 ++++---- src/pages/guides/learn/how_to/use_geometry.md | 20 +++++++------------ 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/pages/guides/learn/how_to/group_elements.md b/src/pages/guides/learn/how_to/group_elements.md index 91d9d16d5..e139e8471 100644 --- a/src/pages/guides/learn/how_to/group_elements.md +++ b/src/pages/guides/learn/how_to/group_elements.md @@ -55,17 +55,17 @@ To create a Group, you can use the [`editor.createGroup()`](../../../references/ ### Example -```js +```js{try id=createBasicGroup} // sandbox/code.js import { editor } from "express-document-sdk"; // Create some Text const greeting = editor.createText("Hiya!"); -greeting.translation = { x: 100, y: 50 }; +greeting.translation = { x: 0, y: 0 }; // Create some other Text const saluto = editor.createText("Ciao!"); -saluto.translation = { x: 100, y: 150 }; +saluto.translation = { x: 0, y: 50 }; // Create a Group šŸ‘ˆ const greetingsGroup = editor.createGroup(); diff --git a/src/pages/guides/learn/how_to/position_elements.md b/src/pages/guides/learn/how_to/position_elements.md index e3d92b3e5..726f2f484 100644 --- a/src/pages/guides/learn/how_to/position_elements.md +++ b/src/pages/guides/learn/how_to/position_elements.md @@ -52,7 +52,7 @@ faq: Let's use this simple Rectangle to demonstrate how to move and rotate elements in Adobe Express. -```js +```js{try id=createAndPositionRectangle} // sandbox/code.js import { editor } from "express-document-sdk"; @@ -60,6 +60,9 @@ const rect = editor.createRectangle(); rect.width = 200; rect.height = 100; +// Move the rectangle 50px to the right and 100px down +rect.translation = { x: 50, y: 100 }; + editor.context.insertionParent.children.append(rect); ``` diff --git a/src/pages/guides/learn/how_to/use_color.md b/src/pages/guides/learn/how_to/use_color.md index 81f75f576..3b43c7c78 100644 --- a/src/pages/guides/learn/how_to/use_color.md +++ b/src/pages/guides/learn/how_to/use_color.md @@ -105,15 +105,15 @@ Colors are not directly applied, instead, to shapes; more generally, they are us If you're confused, worry not! This is the wondrous word of object oriented programming. The following example should clarify things: -```js +```js{try id=applyFillAndStrokeColors} // sandbox/code.js import { editor, colorUtils } from "express-document-sdk"; // Create the shape const ellipse = editor.createEllipse(); -ellipse.width = 100; -ellipse.height = 50; -ellipse.translation = { x: 50, y: 50 }; +ellipse.rx = 100; +ellipse.ry = 50; +ellipse.translation = { x: 150, y: 150 }; // Generate the needed colors const innerColor = colorUtils.fromHex("#A38AF0"); diff --git a/src/pages/guides/learn/how_to/use_geometry.md b/src/pages/guides/learn/how_to/use_geometry.md index 2a27dd649..8814be5e9 100644 --- a/src/pages/guides/learn/how_to/use_geometry.md +++ b/src/pages/guides/learn/how_to/use_geometry.md @@ -48,7 +48,7 @@ Adobe Express provides a set of geometric shapes that you can create and style p ### Example: Add a Rectangle -```js +```js{try id=createBasicRectangle} // sandbox/code.js import { editor } from "express-document-sdk"; @@ -58,11 +58,8 @@ const rect = editor.createRectangle(); rect.width = 100; rect.height = 100; -// The current page, where the rectangle will be placed -const currentPage = editor.context.currentPage; - -// Append the rectangle to the page. -currentPage.artboards.first.children.append(rect); +// Add the rectangle to the document +editor.context.insertionParent.children.append(rect); ``` @@ -91,7 +88,7 @@ Ellipses don't have a `width` and `height` properties, but a [`rx`](../../../ref An ellipse with a radius of 200 on the x-axis and 100 on the y-axis will result in a shape with 400 wide (`rx` times two) and a 200 tall (`ry` times two)! -```js +```js{try id=createBasicEllipse} // sandbox/code.js import { editor } from "express-document-sdk"; @@ -102,11 +99,8 @@ ellipse.ry = 100; // radius y šŸ‘ˆ console.log(ellipse.boundsLocal); // { x: 0, y: 0, width: 400, height: 200 } šŸ‘ˆ mind the actual bounds! -// The current page, where the rectangle will be placed -const currentPage = editor.context.currentPage; - -// Append the rectangle to the page. -currentPage.artboards.first.children.append(rect); +// Add the ellipse to the document +editor.context.insertionParent.children.append(ellipse); ``` ### Example: Style Shapes @@ -152,7 +146,7 @@ Paths are a versatile tool to create complex shapes in Adobe Express. The [`edit ### Example: Single path -```js +```js{try id=createSinglePath} // sandbox/code.js import { editor } from "express-document-sdk"; From 46a03176d3d0be2f112075b01f515a0cd8d8138f Mon Sep 17 00:00:00 2001 From: Holly Schinsky Date: Sun, 23 Nov 2025 12:47:39 -0500 Subject: [PATCH 03/11] Make launch playground button secondary --- src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js b/src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js index 15d01eacd..82d74d9cd 100644 --- a/src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js +++ b/src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js @@ -947,7 +947,7 @@ const GlobalHeader = ({ `}> {location.pathname.includes('/how_to/') && ( - + Launch Playground )} From 858d60243285744e75182a7d1439e0d38967fa85 Mon Sep 17 00:00:00 2001 From: Holly Schinsky Date: Sun, 23 Nov 2025 13:48:34 -0500 Subject: [PATCH 04/11] Fix samples for FFC error in upload --- src/pages/guides/learn/how_to/group_elements.md | 6 +++--- .../guides/learn/how_to/position_elements.md | 4 ++-- src/pages/guides/learn/how_to/use_color.md | 2 +- src/pages/guides/learn/how_to/use_geometry.md | 15 ++++++++------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/pages/guides/learn/how_to/group_elements.md b/src/pages/guides/learn/how_to/group_elements.md index e139e8471..37bf33c07 100644 --- a/src/pages/guides/learn/how_to/group_elements.md +++ b/src/pages/guides/learn/how_to/group_elements.md @@ -67,14 +67,14 @@ greeting.translation = { x: 0, y: 0 }; const saluto = editor.createText("Ciao!"); saluto.translation = { x: 0, y: 50 }; -// Create a Group šŸ‘ˆ +// Create a Group const greetingsGroup = editor.createGroup(); greetingsGroup.translation = { x: 100, y: 100 }; -// Append the Text nodes to the Group šŸ‘ˆ +// Append the Text nodes to the Group greetingsGroup.children.append(greeting, saluto); -// Append the Group to the page šŸ‘ˆ +// Append the Group to the page editor.context.insertionParent.children.append(greetingsGroup); ``` diff --git a/src/pages/guides/learn/how_to/position_elements.md b/src/pages/guides/learn/how_to/position_elements.md index 726f2f484..d9eb2151f 100644 --- a/src/pages/guides/learn/how_to/position_elements.md +++ b/src/pages/guides/learn/how_to/position_elements.md @@ -136,9 +136,9 @@ rect.translation = { x: 50, y: 100 }; rect.setRotationInParent(15, { x: 0, y: 0 }); console.log(rect.boundsLocal); -// {x: 0, y: 0, width: 200, height: 100} šŸ‘ˆ +// {x: 0, y: 0, width: 200, height: 100} console.log("boundsInParent", rect.boundsInParent); -// {x: 24.2, y: 100, width: 219.0, height: 148.3} šŸ‘ˆ +// {x: 24.2, y: 100, width: 219.0, height: 148.3} editor.context.insertionParent.children.append(rect); ``` diff --git a/src/pages/guides/learn/how_to/use_color.md b/src/pages/guides/learn/how_to/use_color.md index 3b43c7c78..53b483942 100644 --- a/src/pages/guides/learn/how_to/use_color.md +++ b/src/pages/guides/learn/how_to/use_color.md @@ -126,7 +126,7 @@ const outerColorStroke = editor.makeStroke({ width: 20, }); -// šŸ‘‡ Apply the fill and stroke +// Apply the fill and stroke ellipse.fill = innerColorFill; ellipse.stroke = outerColorStroke; diff --git a/src/pages/guides/learn/how_to/use_geometry.md b/src/pages/guides/learn/how_to/use_geometry.md index 8814be5e9..560ad1e7a 100644 --- a/src/pages/guides/learn/how_to/use_geometry.md +++ b/src/pages/guides/learn/how_to/use_geometry.md @@ -93,11 +93,11 @@ An ellipse with a radius of 200 on the x-axis and 100 on the y-axis will result import { editor } from "express-document-sdk"; const ellipse = editor.createEllipse(); -ellipse.rx = 200; // radius x šŸ‘ˆ -ellipse.ry = 100; // radius y šŸ‘ˆ +ellipse.rx = 200; // radius x +ellipse.ry = 100; // radius y console.log(ellipse.boundsLocal); -// { x: 0, y: 0, width: 400, height: 200 } šŸ‘ˆ mind the actual bounds! +// { x: 0, y: 0, width: 400, height: 200 } - mind the actual bounds! // Add the ellipse to the document editor.context.insertionParent.children.append(ellipse); @@ -107,7 +107,7 @@ editor.context.insertionParent.children.append(ellipse); Shapes have `fill` and `stroke` properties that you can use to style them. The following example demonstrates how to create an ellipse with a fill and a stroke. -```js{try id=createEllipseWithFillAndStroke} +```js{try id=createEllipseWithFillStroke} // sandbox/code.js import { editor, colorUtils, constants } from "express-document-sdk"; @@ -115,11 +115,12 @@ import { editor, colorUtils, constants } from "express-document-sdk"; const ellipse = editor.createEllipse(); ellipse.rx = 200; ellipse.ry = 100; +ellipse.translation = { x: 250, y: 150 }; -// šŸ‘‡ Apply the fill color +// Apply the fill color ellipse.fill = editor.makeColorFill(colorUtils.fromHex("#F3D988")); -// šŸ‘‡ Create the stroke +// Create the stroke const stroke = editor.makeStroke({ color: colorUtils.fromHex("#E29E4E"), width: 20, @@ -127,7 +128,7 @@ const stroke = editor.makeStroke({ dashPattern: [50, 2], }); -// šŸ‘‡ Apply the stroke +// Apply the stroke ellipse.stroke = stroke; // Add the shape to the document From 5a19e0b0650afb90aed38cb2b646d5b865722eaf Mon Sep 17 00:00:00 2001 From: Holly Schinsky Date: Sun, 23 Nov 2025 17:40:57 -0500 Subject: [PATCH 05/11] Push more try enabled samples --- src/pages/guides/learn/how_to/manage_pages.md | 4 ++-- .../learn/how_to/resize_rescale_elements.md | 17 +++++++++-------- src/pages/guides/learn/how_to/use_text.md | 8 ++++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/pages/guides/learn/how_to/manage_pages.md b/src/pages/guides/learn/how_to/manage_pages.md index 27f6554b5..2dbe1fe03 100644 --- a/src/pages/guides/learn/how_to/manage_pages.md +++ b/src/pages/guides/learn/how_to/manage_pages.md @@ -78,7 +78,7 @@ Use the [`editor.documentRoot.pages.addPage()`](../../../references/document-san #### JavaScript -```js +```js{try id=addStandardPage} // sandbox/code.js import { editor } from "express-document-sdk"; @@ -264,7 +264,7 @@ const lastPage: PageNode = allPages[allPages.length - 1]; #### JavaScript -```js +```js{try id=addContentToNewPage} // sandbox/code.js import { editor } from "express-document-sdk"; diff --git a/src/pages/guides/learn/how_to/resize_rescale_elements.md b/src/pages/guides/learn/how_to/resize_rescale_elements.md index 1adf47020..bc187efb4 100644 --- a/src/pages/guides/learn/how_to/resize_rescale_elements.md +++ b/src/pages/guides/learn/how_to/resize_rescale_elements.md @@ -68,22 +68,24 @@ Rescaling operations maintain the aspect ratio of elements while changing their Use `rescaleProportionalToWidth()` to adjust an element's width while maintaining its aspect ratio. The height will automatically adjust proportionally. -```js +```js{try id=rescaleByWidth} // sandbox/code.js -import { editor } from "express-document-sdk"; +import { editor, colorUtils } from "express-document-sdk"; // Create a rectangle with specific dimensions const rect = editor.createRectangle(); rect.width = 200; rect.height = 100; +rect.translation = { x: 100, y: 100 }; rect.fill = editor.makeColorFill(colorUtils.fromHex("#3498db")); + // Add it to the page editor.context.insertionParent.children.append(rect); // Rescale to 300px width - height becomes 150px automatically rect.rescaleProportionalToWidth(300); -console.log(`New dimensions: ${rect.width} x ${rect.height}`); +console.log("New dimensions:", rect.width, "x", rect.height); // New dimensions: 300 x 150 ``` @@ -93,13 +95,14 @@ console.log(`New dimensions: ${rect.width} x ${rect.height}`); Similarly, use `rescaleProportionalToHeight()` to adjust an element's height while maintaining its aspect ratio. The width will automatically adjust proportionally. -```js +```js{try id=rescaleByHeight} // sandbox/code.js -// import { editor } from "express-document-sdk"; +import { editor, colorUtils } from "express-document-sdk"; const ellipse = editor.createEllipse(); ellipse.rx = 100; // radius x = 100 (width = 200) ellipse.ry = 50; // radius y = 50 (height = 100) +ellipse.translation = { x: 150, y: 100 }; ellipse.fill = editor.makeColorFill(colorUtils.fromHex("#F0B76C")); editor.context.insertionParent.children.append(ellipse); @@ -107,9 +110,7 @@ editor.context.insertionParent.children.append(ellipse); // Rescale to 150px height - width becomes 300px automatically ellipse.rescaleProportionalToHeight(150); -console.log( - `New bounds: ${ellipse.boundsLocal.width} x ${ellipse.boundsLocal.height}` -); +console.log("New bounds:", ellipse.boundsLocal.width, "x", ellipse.boundsLocal.height); // New bounds: 300 x 150 ``` diff --git a/src/pages/guides/learn/how_to/use_text.md b/src/pages/guides/learn/how_to/use_text.md index a1a325a98..3a642f104 100644 --- a/src/pages/guides/learn/how_to/use_text.md +++ b/src/pages/guides/learn/how_to/use_text.md @@ -97,7 +97,7 @@ The `editor.createText()` method accepts a string as a parameter, and returns a ### Example: Create basic Text -```js +```js{try id=createBasicText} // sandbox/code.js import { editor } from "express-document-sdk"; @@ -271,7 +271,7 @@ The `insertText()` method inserts new text at a specific position within the exi #### Example: Basic Text Insertion -```js +```js{try id=insertTextWithColor} // sandbox/code.js import { editor } from "express-document-sdk"; @@ -289,7 +289,7 @@ textNode.setPositionInParent( // Add the TextNode to the document insertionParent.children.append(textNode); -// Insert text at position 10 +// Insert text at position 6 contentModel.insertText( "Express ", 6, @@ -351,7 +351,7 @@ The `appendText()` method adds new text to the end of the existing content. It a #### Example: Append Text -```js +```js{try id=appendTextMultiple} // sandbox/code.js import { editor } from "express-document-sdk"; From f3dd22a7ff00119387ca86f8e7f18e7cc6c6b7e1 Mon Sep 17 00:00:00 2001 From: Holly Schinsky Date: Sun, 23 Nov 2025 18:09:07 -0500 Subject: [PATCH 06/11] Added more Try snippets --- src/pages/guides/learn/how_to/use_color.md | 31 ++++++++++++------- src/pages/guides/learn/how_to/use_geometry.md | 2 +- src/pages/guides/learn/how_to/use_text.md | 9 +++--- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/pages/guides/learn/how_to/use_color.md b/src/pages/guides/learn/how_to/use_color.md index 53b483942..cc8c51d1b 100644 --- a/src/pages/guides/learn/how_to/use_color.md +++ b/src/pages/guides/learn/how_to/use_color.md @@ -57,17 +57,23 @@ Colors in Adobe Express are created as instances of the [`Color`](../../../refer The entrypoint for creating colors is the [`colorUtils`](../../../references/document-sandbox/document-apis/classes/ColorUtils.md) class, imported from the `"express-document-sdk"`, so we're talking about [Document APIs](../../../references/document-sandbox/document-apis/index.md) here. Especially the static [`fromRGB()`](../../../references/document-sandbox/document-apis/classes/ColorUtils.md#fromrgb) and [`fromHex()`](../../../references/document-sandbox/document-apis/classes/ColorUtils.md#fromhex) methods. -```js +```js{try id=createColors} // sandbox/code.js import { editor, colorUtils } from "express-document-sdk"; -// Alpha is optional, defaults to 1 +// Create colors from RGB (values from 0 to 1) const red = colorUtils.fromRGB(1, 0, 0); + +// Create colors from HEX const green = colorUtils.fromHex("#00FF00"); -// With alpha -const feldgrau = colorUtils.fromRGB(0.28, 0.32, 0.39, 0.5); // 50% opacity -const heliotrope = colorUtils.fromHex("#C768F780"); // 50% opacity +// Create a rectangle and apply the red color +const rect = editor.createRectangle(); +rect.width = 200; +rect.height = 100; +rect.translation = { x: 100, y: 100 }; +rect.fill = editor.makeColorFill(red); +editor.context.insertionParent.children.append(rect); ``` In case you need it, you can also convert a color to a HEX string using the [`toHex()`](../../../references/document-sandbox/document-apis/classes/ColorUtils.md#tohex) method. Please note that the alpha value is always included in the output string. @@ -83,17 +89,20 @@ You can directly set the `color` property of a Text node via [`applyCharacterSty ### Example: Text color -```js +```js{try id=applyTextColor} // sandbox/code.js import { editor, colorUtils } from "express-document-sdk"; -// Assuming the user has selected a text frame -const textNode = editor.context.selection[0]; +// Create a text node +const textNode = editor.createText("Hello, World!"); +const insertionParent = editor.context.insertionParent; +textNode.translation = { x: 100, y: 100 }; +insertionParent.children.append(textNode); -// Apply character styles to the first three letters +// Apply character styles to the first five letters textNode.fullContent.applyCharacterStyles( - { color: colorUtils.fromHex("#E1A141") }, // šŸ‘ˆ - { start: 0, length: 3 } + { color: colorUtils.fromHex("#E1A141") }, + { start: 0, length: 5 } ); ``` diff --git a/src/pages/guides/learn/how_to/use_geometry.md b/src/pages/guides/learn/how_to/use_geometry.md index 560ad1e7a..a4a211ab2 100644 --- a/src/pages/guides/learn/how_to/use_geometry.md +++ b/src/pages/guides/learn/how_to/use_geometry.md @@ -164,7 +164,7 @@ editor.context.insertionParent.children.append(p1); Combining and grouping multiple paths, you can create complex shapes, like in the following example: -```js +```js{try id=createMultiplePaths} // sandbox/code.js import { editor } from "express-document-sdk"; diff --git a/src/pages/guides/learn/how_to/use_text.md b/src/pages/guides/learn/how_to/use_text.md index 3a642f104..ad143b3a4 100644 --- a/src/pages/guides/learn/how_to/use_text.md +++ b/src/pages/guides/learn/how_to/use_text.md @@ -149,7 +149,7 @@ Although possible, it's not recommended to replace the text content of a `TextNo #### Example: Basic Text Replacement -```js +```js{try id=replaceTextBasic} // sandbox/code.js import { editor } from "express-document-sdk"; @@ -386,9 +386,9 @@ The `deleteText()` method removes a specific range of text from the content. It #### Example: Delete Text Range -```js +```js{try id=deleteTextRange} // sandbox/code.js -import { editor, constants } from "express-document-sdk"; +import { editor } from "express-document-sdk"; // Create a new TextNode const textNode = editor.createText("It's Friday, don't deploy to Production!"); @@ -404,7 +404,7 @@ textNode.setPositionInParent( // Add the TextNode to the document insertionParent.children.append(textNode); -// Delete 13 characters starting at position 6 +// Delete 6 characters starting at position 13 contentModel.deleteText({ start: 13, length: 6 }); // It's Friday, don't deploy to Production! @@ -413,7 +413,6 @@ contentModel.deleteText({ start: 13, length: 6 }); // It's Friday, deploy to Production! // You can delete multiple ranges calling the method as many times as needed - contentModel.deleteText({ start: 12, length: 22 }); // It's Friday! From 5858b99defac1795fede46a3a4d11cd0d183ca54 Mon Sep 17 00:00:00 2001 From: Holly Schinsky Date: Sun, 23 Nov 2025 19:01:39 -0500 Subject: [PATCH 07/11] Fixed list, other misc --- src/pages/guides/learn/how_to/index.md | 7 +++++-- src/pages/guides/learn/how_to/resize_rescale_elements.md | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/pages/guides/learn/how_to/index.md b/src/pages/guides/learn/how_to/index.md index e7fb55014..426a760a5 100644 --- a/src/pages/guides/learn/how_to/index.md +++ b/src/pages/guides/learn/how_to/index.md @@ -24,9 +24,9 @@ The following guides contain a set of common use cases and accompanying code sni -Code Playground integration is coming soon! +Try samples in Code Playground! -The [Code Playground](../../getting_started/code_playground.md) is the perfect tool to test each how-to snippet. We're currently working on a leaner integration between the Playground and our documentation; running the code will be even easier soon! +Many how-to guides now include a **"Try"** button that opens the code sample directly in the [Code Playground](../../getting_started/code_playground.md). Click the button to instantly test Document API samples in Script mode without setting up a local development environment! We're constantly adding new how-tos, so make sure to check back often. If you're looking for Tutorials that guide you in building add-ons from the ground up, please check the [Complete Projects](#complete-projects) section. @@ -78,6 +78,9 @@ We're constantly adding new how-tos, so make sure to check back often. If you're | | [Use PDF and PowerPoint](./use_pdf_powerpoint.md) | | | [Group Elements](./group_elements.md) | | | [Position Elements](./position_elements.md) | +| | [Resize & Rescale Elements](./resize_rescale_elements.md) | +| | [Handle Selection](./handle_selection.md) | +| Manage Pages | [Manage Pages](./manage_pages.md) | | Use Metadata | [Document Metadata](./document_metadata.md) | | | [Page Metadata](./page_metadata.md) | | | [Element Metadata](./element_metadata.md) | diff --git a/src/pages/guides/learn/how_to/resize_rescale_elements.md b/src/pages/guides/learn/how_to/resize_rescale_elements.md index bc187efb4..746512904 100644 --- a/src/pages/guides/learn/how_to/resize_rescale_elements.md +++ b/src/pages/guides/learn/how_to/resize_rescale_elements.md @@ -85,7 +85,7 @@ editor.context.insertionParent.children.append(rect); // Rescale to 300px width - height becomes 150px automatically rect.rescaleProportionalToWidth(300); -console.log("New dimensions:", rect.width, "x", rect.height); +console.log(`New dimensions: ${rect.width} x ${rect.height}`); // New dimensions: 300 x 150 ``` @@ -110,7 +110,7 @@ editor.context.insertionParent.children.append(ellipse); // Rescale to 150px height - width becomes 300px automatically ellipse.rescaleProportionalToHeight(150); -console.log("New bounds:", ellipse.boundsLocal.width, "x", ellipse.boundsLocal.height); +console.log(`New bounds: ${ellipse.boundsLocal.width} x ${ellipse.boundsLocal.height}`); // New bounds: 300 x 150 ``` From b505b27d05358b95e97c3112c99741d4712c24b8 Mon Sep 17 00:00:00 2001 From: Holly Schinsky Date: Sun, 23 Nov 2025 19:47:01 -0500 Subject: [PATCH 08/11] Added retry/error handling to upload script for intermittent errors --- upload-playground-samples.mjs | 158 ++++++++++++++++++++++------------ 1 file changed, 101 insertions(+), 57 deletions(-) mode change 100644 => 100755 upload-playground-samples.mjs diff --git a/upload-playground-samples.mjs b/upload-playground-samples.mjs old mode 100644 new mode 100755 index 2b0c5cf5a..4abf24fec --- a/upload-playground-samples.mjs +++ b/upload-playground-samples.mjs @@ -111,69 +111,89 @@ async function createZipFileFromCodeBlock(block) { } /** - * Upload a code block to FFC. + * Upload a code block to FFC with retry logic. * @param block - The code block to store. * @param projectId - The project ID corresponding to the code block. + * @param maxRetries - Maximum number of retry attempts (default: 3). * @returns the response from the FFC API. */ -async function uploadCodeBlockToFFC(codeBlock, projectId) { - try { - // Process the code and log it for verification - const processedCode = commentOutExpressDocumentSDKImports(codeBlock.code); - - console.log("\n" + "=".repeat(80)); - console.log(`Uploading code block: ${projectId}`); - console.log("File: " + codeBlock.filePath); - console.log("-".repeat(80)); - console.log("Processed code that will be uploaded:"); - console.log("-".repeat(80)); - console.log(processedCode); - console.log("=".repeat(80) + "\n"); - - const accessToken = await getImsServiceToken(); - const url = new URL( - `${FFC_PLAYGROUND_ENDPOINT}/${projectId}`, - FFC_BASE_URL - ); - - // Create zip with the already-processed code - const zip = new JSZip(); - zip.file("script.js", processedCode); - const zipBuffer = await zip.generateAsync({ type: "nodebuffer" }); - - const form = new FormData(); - form.append( - "file", - new Blob([zipBuffer], { type: "application/zip" }), - `${projectId}.zip` - ); - form.append("name", projectId); - - const response = await fetch(url, { - method: "PUT", - headers: { - Accept: "application/vnd.adobe-ffcaddon.response+json", - Authorization: `Bearer ${accessToken}`, - "x-api-key": PLAYGROUND_API_KEY, - }, - body: form, - }); +async function uploadCodeBlockToFFC(codeBlock, projectId, maxRetries = 3) { + let lastError; + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + // Process the code and log it for verification + const processedCode = commentOutExpressDocumentSDKImports(codeBlock.code); + + if (attempt === 1) { + console.log(`\nšŸ“¤ Uploading: ${projectId} (from ${codeBlock.filePath})`); + } else { + console.log(` Retry ${attempt - 1}/${maxRetries - 1}: ${projectId}`); + } - if (!response.ok) { - const text = await response.text(); - const requestId = response.headers.get(FFC_REQUEST_ID); - console.log("FFC Request ID:", requestId); - throw new Error( - `Failed to upload code block to FFC - HTTP ${response.status}: ${text}` + const accessToken = await getImsServiceToken(); + const url = new URL( + `${FFC_PLAYGROUND_ENDPOINT}/${projectId}`, + FFC_BASE_URL + ); + + // Create zip with the already-processed code + const zip = new JSZip(); + zip.file("script.js", processedCode); + const zipBuffer = await zip.generateAsync({ type: "nodebuffer" }); + + const form = new FormData(); + form.append( + "file", + new Blob([zipBuffer], { type: "application/zip" }), + `${projectId}.zip` ); + form.append("name", projectId); + + const response = await fetch(url, { + method: "PUT", + headers: { + Accept: "application/vnd.adobe-ffcaddon.response+json", + Authorization: `Bearer ${accessToken}`, + "x-api-key": PLAYGROUND_API_KEY, + }, + body: form, + }); + + if (!response.ok) { + const text = await response.text(); + const requestId = response.headers.get(FFC_REQUEST_ID); + + // Log request ID for debugging + if (requestId) { + console.log(` FFC Request ID: ${requestId}`); + } + + throw new Error( + `Failed to upload code block to FFC - HTTP ${response.status}: ${text}` + ); + } + + console.log(`āœ… Successfully uploaded: ${projectId}`); + return response.json(); + + } catch (error) { + lastError = error; + + // If this was the last attempt, don't retry + if (attempt === maxRetries) { + console.error(`āŒ Failed to upload (${projectId}) after ${maxRetries} attempts: ${error.message}`); + throw error; + } + + // Exponential backoff: wait 2^attempt seconds + const waitTime = Math.pow(2, attempt) * 1000; + console.log(` ā³ Waiting ${waitTime / 1000}s before retry...`); + await new Promise(resolve => setTimeout(resolve, waitTime)); } - - console.log(`āœ… Successfully uploaded: ${projectId}\n`); - return response.json(); - } catch (error) { - console.error(`āŒ Failed to upload code block to FFC (${projectId}):`, error.message); - throw error; } + + throw lastError; } /** @@ -232,19 +252,43 @@ function extractCodeBlocks(content, filePath) { * Main function to run the code block extractor. * 1. Find all markdown files in the pages directory. * 2. Extract code blocks from each markdown file. - * 3. Store each code block in the backend API. + * 3. Store each code block in the backend API with retry logic. */ async function run() { const markdownFiles = await findMarkdownFiles(PAGES_DIRECTORY); + let successCount = 0; + let failureCount = 0; for (const filePath of markdownFiles) { const content = await fs.readFile(filePath, "utf8"); const codeBlocks = extractCodeBlocks(content, filePath); for (const codeBlock of codeBlocks) { - await uploadCodeBlockToFFC(codeBlock, codeBlock.id); + try { + await uploadCodeBlockToFFC(codeBlock, codeBlock.id); + successCount++; + + // Small delay between uploads to avoid overwhelming the backend + await new Promise(resolve => setTimeout(resolve, 500)); + } catch (error) { + failureCount++; + console.error(`Skipping ${codeBlock.id} due to upload failure.`); + // Continue with next upload instead of failing entire script + } } } + + console.log(`\n${"=".repeat(80)}`); + console.log(`šŸ“Š Upload Summary:`); + console.log(` āœ… Successful: ${successCount}`); + console.log(` āŒ Failed: ${failureCount}`); + console.log(` šŸ“¦ Total: ${successCount + failureCount}`); + console.log("=".repeat(80)); + + if (failureCount > 0) { + console.log(`\nāš ļø Some uploads failed. You may need to run the script again.`); + process.exit(1); + } } run().catch((error) => { From d34808eb1b1607bb20b401ff22339bf44de83e8a Mon Sep 17 00:00:00 2001 From: Holly Schinsky Date: Sun, 23 Nov 2025 21:08:04 -0500 Subject: [PATCH 09/11] Fix checks error on yarn.lock --- yarn.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index fd612c0b5..99415f95a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8211,7 +8211,7 @@ __metadata: dependencies: "@adobe/gatsby-theme-aio": ^4.15.1 dotenv: 17.2.2 - fs-extra: ^11.3.2 + fs-extra: 11.3.2 gatsby: 4.22.0 jszip: 3.10.1 markdownlint: ^0.35.0 @@ -10103,25 +10103,25 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^10.1.0": - version: 10.1.0 - resolution: "fs-extra@npm:10.1.0" +"fs-extra@npm:11.3.2": + version: 11.3.2 + resolution: "fs-extra@npm:11.3.2" dependencies: graceful-fs: ^4.2.0 jsonfile: ^6.0.1 universalify: ^2.0.0 - checksum: dc94ab37096f813cc3ca12f0f1b5ad6744dfed9ed21e953d72530d103cea193c2f81584a39e9dee1bea36de5ee66805678c0dddc048e8af1427ac19c00fffc50 + checksum: 24a7a6e09668add7f74bf6884086b860ce39c7883d94f564623d4ca5c904ff9e5e33fa6333bd3efbf3528333cdedf974e49fa0723e9debf952f0882e6553d81e languageName: node linkType: hard -"fs-extra@npm:^11.3.2": - version: 11.3.2 - resolution: "fs-extra@npm:11.3.2" +"fs-extra@npm:^10.1.0": + version: 10.1.0 + resolution: "fs-extra@npm:10.1.0" dependencies: graceful-fs: ^4.2.0 jsonfile: ^6.0.1 universalify: ^2.0.0 - checksum: 24a7a6e09668add7f74bf6884086b860ce39c7883d94f564623d4ca5c904ff9e5e33fa6333bd3efbf3528333cdedf974e49fa0723e9debf952f0882e6553d81e + checksum: dc94ab37096f813cc3ca12f0f1b5ad6744dfed9ed21e953d72530d103cea193c2f81584a39e9dee1bea36de5ee66805678c0dddc048e8af1427ac19c00fffc50 languageName: node linkType: hard From 532c70005569f5444a77c84f91c84735494f9301 Mon Sep 17 00:00:00 2001 From: Holly Schinsky Date: Mon, 24 Nov 2025 14:13:55 -0500 Subject: [PATCH 10/11] Updated global button and Try button words/styles --- src/@adobe/gatsby-theme-aio/components/Code/index.js | 2 +- src/@adobe/gatsby-theme-aio/components/Code/styles.css | 4 ++-- src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/@adobe/gatsby-theme-aio/components/Code/index.js b/src/@adobe/gatsby-theme-aio/components/Code/index.js index 23c988199..1ca2510f6 100644 --- a/src/@adobe/gatsby-theme-aio/components/Code/index.js +++ b/src/@adobe/gatsby-theme-aio/components/Code/index.js @@ -164,7 +164,7 @@ const Code = ({ children, className = "", theme, metastring = "" }) => { !isMobile && openCodePlayground(children, sampleId) } > - Try + Try in playground )} diff --git a/src/@adobe/gatsby-theme-aio/components/Code/styles.css b/src/@adobe/gatsby-theme-aio/components/Code/styles.css index c4543201b..8470f97b7 100644 --- a/src/@adobe/gatsby-theme-aio/components/Code/styles.css +++ b/src/@adobe/gatsby-theme-aio/components/Code/styles.css @@ -40,7 +40,7 @@ } .code-copy-button.with-try { - inset-inline-end: var(--spectrum-global-dimension-size-700); + inset-inline-end: calc(var(--spectrum-global-dimension-size-2000) + var(--spectrum-global-dimension-size-100)); } .code-try-button { @@ -67,7 +67,7 @@ } .code-tooltip-container.with-try { - inset-inline-end: calc(var(--spectrum-global-dimension-size-700) + var(--spectrum-global-dimension-size-600) + var(--spectrum-global-dimension-size-125)); + inset-inline-end: calc(var(--spectrum-global-dimension-size-2000) + var(--spectrum-global-dimension-size-100) + var(--spectrum-global-dimension-size-600) + var(--spectrum-global-dimension-size-125)); } .code-tooltip { diff --git a/src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js b/src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js index 82d74d9cd..bd6d72af6 100644 --- a/src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js +++ b/src/@adobe/gatsby-theme-aio/components/GlobalHeader/index.js @@ -947,8 +947,8 @@ const GlobalHeader = ({ `}> {location.pathname.includes('/how_to/') && ( - - Launch Playground + + Code Playground )} From 3a60fddfa0fe5b18ecc34fc421cae33519fbc52b Mon Sep 17 00:00:00 2001 From: Holly Schinsky Date: Mon, 24 Nov 2025 14:47:55 -0500 Subject: [PATCH 11/11] Styles the launch button in hello-world --- src/styles.css | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/styles.css b/src/styles.css index b9114fdeb..b7e78830e 100644 --- a/src/styles.css +++ b/src/styles.css @@ -190,3 +190,20 @@ section:has(> div > p > span > picture) { .developers-live-announcement a:hover span { color: #fff !important; } + +.open-playground-button { + padding: 10px 20px; + border-radius: 20px; + font-weight: 700; + border: 2px solid #000000; + background-color: #ffffff; + cursor: pointer; + font-family: "adobe-clean" !important; +} + +.playground-button-container { + display: flex; + justify-content: center; + align-items: center; + margin: 20px 0; +}