diff --git a/.github/workflows/publish-sample-add-ons.yml b/.github/workflows/publish-sample-add-ons.yml index dcfa585..9d2bda0 100644 --- a/.github/workflows/publish-sample-add-ons.yml +++ b/.github/workflows/publish-sample-add-ons.yml @@ -3,7 +3,7 @@ name: Deploy sample Add-ons to GitHub Pages on: # Runs on pushes targeting the default branch push: - branches: ["main"] + branches: ['main'] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -17,7 +17,7 @@ permissions: # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. concurrency: - group: "pages" + group: 'pages' cancel-in-progress: false jobs: @@ -29,21 +29,21 @@ jobs: node-version: [22.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - cache-dependency-path: addons-web-sdk/samples/package-lock.json - - run: npm run build - working-directory: addons-web-sdk/samples - - name: Upload build - uses: actions/upload-artifact@v4 - with: - name: page - path: addons-web-sdk/samples/dist - if-no-files-found: error + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + cache-dependency-path: addons-web-sdk/samples/package-lock.json + - run: npm run build + working-directory: addons-web-sdk/samples + - name: Upload build + uses: actions/upload-artifact@v4 + with: + name: page + path: addons-web-sdk/samples/dist + if-no-files-found: error deploy: runs-on: ubuntu-latest needs: build diff --git a/.gitignore b/.gitignore index 2ef5870..d9b3c62 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ # Build outputs for sample Meet Add-ons -**/dist +**/dist/*/ **/node_modules **/.DS_Store diff --git a/addons-web-sdk/samples/README.md b/addons-web-sdk/samples/README.md index 76295f6..0c8da78 100644 --- a/addons-web-sdk/samples/README.md +++ b/addons-web-sdk/samples/README.md @@ -3,7 +3,8 @@ This directory contains code for example add-ons, which you can fork to create your own. -| Sample | Description | -| ------------ | ----------------------------------------------------- | -| hello-world | Vanilla JS; renders a hello world div. | -| hello-world-next-js | React + TS + NextJS; renders a hello world div. | +| Sample | Description | +| ------------------- | --------------------------------------------------------------------- | +| hello-world | Vanilla JS; renders a hello world div. | +| hello-world-next-js | React + TS + Next.js; renders a hello world div. | +| animation-next-js | React + TS + Next.js; uses more add-on features to show an animation. | diff --git a/addons-web-sdk/samples/animation-next-js/.env.development b/addons-web-sdk/samples/animation-next-js/.env.development new file mode 100644 index 0000000..d188874 --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/.env.development @@ -0,0 +1 @@ +NEXT_PUBLIC_DEBUG=1 diff --git a/addons-web-sdk/samples/animation-next-js/.gitignore b/addons-web-sdk/samples/animation-next-js/.gitignore new file mode 100644 index 0000000..dbe7685 --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/.gitignore @@ -0,0 +1,5 @@ +# next.js +/.next +out + +certificates diff --git a/addons-web-sdk/samples/animation-next-js/README.md b/addons-web-sdk/samples/animation-next-js/README.md new file mode 100644 index 0000000..8fe5f59 --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/README.md @@ -0,0 +1,5 @@ +# Pretty Colors: Next.js Add-on + +This is a [Meet Add-on](https://developers.google.com/meet/add-ons/guides/overview) built in [Next.js](https://nextjs.org/). This add-on displays an animation that is intended to create a simple "shimmer" effect based on the color that each call participant selects. This add-on only exists to show more features of Google Meet Add-ons than can be found at [googleworkspace/meet/addons-web-sdk/samples/hello-world-next-js](https://github.com/googleworkspace/meet/tree/main/addons-web-sdk/samples/hello-world-next-js). If you find anything about the configuration confusing, please see that more basic example. + +This add-on is deployed with GitHub pages, so that you can view the live versions of its [Side Panel](https://googleworkspace.github.io/meet/animation-next-js/sidepanel), [Main Stage](https://googleworkspace.github.io/meet/animation-next-js/mainstage), and all other routes. The screensharing promotion at the [index.html](https://googleworkspace.github.io/meet/animation-next-js/) will not fully work diff --git a/addons-web-sdk/samples/animation-next-js/next-env.d.ts b/addons-web-sdk/samples/animation-next-js/next-env.d.ts new file mode 100644 index 0000000..d62b474 --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/next-env.d.ts @@ -0,0 +1,19 @@ +// Copyright 2024 Google LLC +// +// Licensed 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/addons-web-sdk/samples/animation-next-js/next.config.mjs b/addons-web-sdk/samples/animation-next-js/next.config.mjs new file mode 100644 index 0000000..3836012 --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/next.config.mjs @@ -0,0 +1,8 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + basePath: '/meet/animation-next-js', + distDir: '../dist/animation-next-js', + output: 'export', +}; + +export default nextConfig; diff --git a/addons-web-sdk/samples/animation-next-js/package.json b/addons-web-sdk/samples/animation-next-js/package.json new file mode 100644 index 0000000..65b567c --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/package.json @@ -0,0 +1,23 @@ +{ + "name": "animation-next-js", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev --experimental-https", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@googleworkspace/meet-addons": "^0.12.0", + "next": "14.2.7", + "react": "^18", + "react-dom": "^18" + }, + "devDependencies": { + "@types/node": "^20", + "@types/react": "^18", + "@types/react-dom": "^18", + "typescript": "^5" + } +} diff --git a/addons-web-sdk/samples/animation-next-js/src/app/activitysidepanel/page.tsx b/addons-web-sdk/samples/animation-next-js/src/app/activitysidepanel/page.tsx new file mode 100644 index 0000000..a8146f4 --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/src/app/activitysidepanel/page.tsx @@ -0,0 +1,59 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { + meet, + MeetSidePanelClient, +} from '@googleworkspace/meet-addons/meet.addons'; +import { CLOUD_PROJECT_NUMBER } from '../../shared/constants'; + +/** + * This page displays in the Side Panel after the activity has started to allow + * each call participant to modify the base color of the main stage animation. + */ +export default function Page() { + const [sidePanelClient, setSidePanelClient] = useState(); + + /** + * Sends a newly chosen color to the main stage, using frame-to-frame + * messaging. + * @param newColor Hex code of the new color. + */ + async function updateColor(newColor: string) { + if (!sidePanelClient) { + throw new Error('Side Panel is not yet initialized!'); + } + + await sidePanelClient.notifyMainStage(newColor); + } + + useEffect(() => { + /** + * Initializes the Add-on Side Panel Client. + * https://developers.google.com/meet/add-ons/reference/websdk/addon_sdk.meetsidepanelclient + */ + async function initializeSidePanelClient() { + const session = await meet.addon.createAddonSession({ + cloudProjectNumber: CLOUD_PROJECT_NUMBER, + }); + const client = await session.createSidePanelClient(); + setSidePanelClient(client); + } + initializeSidePanelClient(); + }, []); + + return ( + <> + + updateColor(e.target.value)} + /> + + ); +} diff --git a/addons-web-sdk/samples/animation-next-js/src/app/layout.tsx b/addons-web-sdk/samples/animation-next-js/src/app/layout.tsx new file mode 100644 index 0000000..76ffedb --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/src/app/layout.tsx @@ -0,0 +1,18 @@ +import { type Metadata } from 'next'; + +export const metadata: Metadata = { + title: 'Pretty Colors', + description: 'Google Meet Add-on that shows an animation', +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/addons-web-sdk/samples/animation-next-js/src/app/mainstage/page.tsx b/addons-web-sdk/samples/animation-next-js/src/app/mainstage/page.tsx new file mode 100644 index 0000000..a5bd084 --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/src/app/mainstage/page.tsx @@ -0,0 +1,71 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { + meet, + FrameToFrameMessage, + MeetMainStageClient, +} from '@googleworkspace/meet-addons/meet.addons'; +import { CLOUD_PROJECT_NUMBER } from '../../shared/constants'; +import PrettyColors from '@/components/prettyColors'; + +/** + * @see {@link https://developers.google.com/meet/add-ons/guides/overview#main-stage} + */ +export default function Page() { + const [color, setColor] = useState('#00ff00'); + + /** + * Creates a MeetMainStageClient to control the main stage of the add-on. + * https://developers.google.com/meet/add-ons/reference/websdk/addon_sdk.meetmainstageclient + */ + async function initializeMainStageClient(): Promise { + const session = await meet.addon.createAddonSession({ + cloudProjectNumber: CLOUD_PROJECT_NUMBER, + }); + return await session.createMainStageClient(); + } + + /** + * Parses the collaboration starting state from the side panel, and updates + * the color used for the main animation. + */ + async function setStartingState(mainStageClient: MeetMainStageClient) { + const startingState = await mainStageClient.getActivityStartingState(); + const additionalData = JSON.parse(startingState.additionalData ?? '{}'); + setColor(additionalData.startingColor); + } + + /** + * Listens for new frame-to-frame messages from the side panel that update + * the color used for the main animation. + */ + function awaitNewColor(mainStageClient: MeetMainStageClient) { + mainStageClient.on( + 'frameToFrameMessage', + (message: FrameToFrameMessage) => { + setColor(message.payload); + } + ); + } + + useEffect(() => { + /** + * Initialize the main stage by initializing the client, then using that + * client to get the starting state (color), and observe any new colors + * passed from the Side Panel. + */ + async function initializeMainStage() { + const client = await initializeMainStageClient(); + setStartingState(client); + awaitNewColor(client); + } + initializeMainStage(); + }, []); + + return ( + <> + + + ); +} diff --git a/addons-web-sdk/samples/animation-next-js/src/app/page.tsx b/addons-web-sdk/samples/animation-next-js/src/app/page.tsx new file mode 100644 index 0000000..ab0f86b --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/src/app/page.tsx @@ -0,0 +1,29 @@ +'use client'; + +import { useEffect } from 'react'; +import { meet } from '@googleworkspace/meet-addons/meet.addons.screenshare'; +import { CLOUD_PROJECT_NUMBER, SIDE_PANEL_URL } from '../shared/constants'; + +export default function App() { + /** + * Screensharing this page will prompt you to install/open this add-on. + * When it is opened, it will prompt you to set up the add-on in the side + * panel before starting the activity for everyone. + * @see {@link https://developers.google.com/meet/add-ons/guides/screen-sharing} + */ + useEffect(() => { + meet.addon.screensharing.exposeToMeetWhenScreensharing({ + cloudProjectNumber: CLOUD_PROJECT_NUMBER, + // Will open the Side Panel for the activity initiator to set the + // activity starting state. Activity won't start for other participants. + sidePanelUrl: SIDE_PANEL_URL, + startActivityOnOpen: false, + }); + }, []); + + return ( + <> +
Screenshare this page to promote an add-on.
+ + ); +} diff --git a/addons-web-sdk/samples/animation-next-js/src/app/sidepanel/page.tsx b/addons-web-sdk/samples/animation-next-js/src/app/sidepanel/page.tsx new file mode 100644 index 0000000..426dd08 --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/src/app/sidepanel/page.tsx @@ -0,0 +1,75 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { + meet, + MeetSidePanelClient, +} from '@googleworkspace/meet-addons/meet.addons'; +import { + ACTIVITY_SIDE_PANEL_URL, + CLOUD_PROJECT_NUMBER, + MAIN_STAGE_URL, +} from '../../shared/constants'; + +/** + * @see {@link https://developers.google.com/meet/add-ons/guides/overview#side-panel} + */ +export default function Page() { + const [sidePanelClient, setSidePanelClient] = useState(); + + /** + * Starts the add-on activity and passes the selected color to the Main Stage, + * as part of the activity starting state. + */ + async function startCollaboration(e: unknown) { + if (!sidePanelClient) { + throw new Error('Side Panel is not yet initialized!'); + } + + const startingColor = ( + document.getElementById('starting-color')! as HTMLInputElement + ).value; + await sidePanelClient.startActivity({ + mainStageUrl: MAIN_STAGE_URL, + sidePanelUrl: ACTIVITY_SIDE_PANEL_URL, + // Pass the selected color to customize the initial display. + additionalData: `{\"startingColor\": \"${startingColor}\"}`, + }); + window.location.replace(ACTIVITY_SIDE_PANEL_URL + window.location.search); + } + + useEffect(() => { + /** + * Initializes the Add-on Side Panel Client. + * https://developers.google.com/meet/add-ons/reference/websdk/addon_sdk.meetsidepanelclient + */ + async function initializeSidePanelClient() { + const session = await meet.addon.createAddonSession({ + cloudProjectNumber: CLOUD_PROJECT_NUMBER, + }); + const client = await session.createSidePanelClient(); + setSidePanelClient(client); + } + initializeSidePanelClient(); + }, []); + + return ( + <> +
+ Welcome to Pretty Colors! This is a contrived demo add-on that lets you + look at an animation involving your favorite color. +
+ + +
+ + + ); +} diff --git a/addons-web-sdk/samples/animation-next-js/src/components/prettyColors.css b/addons-web-sdk/samples/animation-next-js/src/components/prettyColors.css new file mode 100644 index 0000000..f3b60f0 --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/src/components/prettyColors.css @@ -0,0 +1,37 @@ +html, +body { + height: 100%; + margin: 0; + width: 100%; +} + +.prettyColorsContainer { + height: 100%; + overflow: hidden; + position: relative; + width: 100%; +} + +.prettyLine { + animation-name: spin; + animation-direction: normal; + animation-duration: 10000ms; + animation-iteration-count: infinite; + animation-timing-function: linear; + height: 10px; + position: absolute; + width: 40%; +} + +.prettyLine:hover { + animation-direction: reverse; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/addons-web-sdk/samples/animation-next-js/src/components/prettyColors.tsx b/addons-web-sdk/samples/animation-next-js/src/components/prettyColors.tsx new file mode 100644 index 0000000..29cdfb3 --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/src/components/prettyColors.tsx @@ -0,0 +1,143 @@ +/** + * This file isn't necessary to understand what is going on with Meet Add-ons. + * It just gives us something fun to look at while testing the add-on code :) + */ + +import { ReactElement } from 'react'; +import './prettyColors.css'; + +type Props = { + // Hex color picked within color input component. + baseColor: string; +}; + +// Copied from https://github.com/google/closure-library/blob/master/closure/goog/color/color.js#L562 +// To avoid installing full closure dep. +function hexToHsl(hexColor: string) { + const rgb = parseInt(hexColor.slice(1), 16); + const r = rgb >> 16; + const g = (rgb >> 8) & 255; + const b = rgb & 255; + const normR = r / 255; + const normG = g / 255; + const normB = b / 255; + const max = Math.max(normR, normG, normB); + const min = Math.min(normR, normG, normB); + let h = 0; + let s = 0; + + // Luminosity is the average of the max and min rgb color intensities. + const l = 0.5 * (max + min); + + // The hue and saturation are dependent on which color intensity is the max. + // If max and min are equal, the color is gray and h and s should be 0. + if (max != min) { + if (max == normR) { + h = (60 * (normG - normB)) / (max - min); + } else if (max == normG) { + h = (60 * (normB - normR)) / (max - min) + 120; + } else if (max == normB) { + h = (60 * (normR - normG)) / (max - min) + 240; + } + + if (0 < l && l <= 0.5) { + s = (max - min) / (2 * l); + } else { + s = (max - min) / (2 - 2 * l); + } + } + // Make sure the hue falls between 0 and 360. + return [Math.round(h + 360) % 360, s, l]; +} + +/** + * Causes the lines to spin in the opposite direction for 1 second, so that + * mousing over the main animation does something. + */ +function reverseAnimation(e: React.MouseEvent) { + const div = e.target as HTMLDivElement; + div.style.animationDirection = 'reverse'; + div.style.animationDuration = '1000ms'; + + setTimeout(() => { + div.style.animationDirection = 'normal'; + div.style.animationDuration = '10000ms'; + }, 1000); +} + +/** + * Draw steps^3 divs that all are shades of the given hslColor and + * all will spin. + */ +function createLines(hslColor: number[], steps: number) { + const coloredLines = []; + const minSaturation = hslColor[1] / 2; + const maxSaturation = Math.max(1, hslColor[1] * 1.5); + const minLuminosity = hslColor[2] / 2; + const maxLuminosity = Math.max(0.9, hslColor[2] * 1.5); + let key = 0; + + for (let i = 0; i < steps; i++) { + for ( + let saturation = minSaturation; + saturation <= maxSaturation; + saturation += (maxSaturation - minSaturation) / steps + ) { + for ( + let luminosity = minLuminosity; + luminosity <= maxLuminosity; + luminosity += (maxLuminosity - minLuminosity) / steps + ) { + coloredLines.push( + createLine(hslColor[0], saturation, luminosity, key++) + ); + } + } + } + return coloredLines; +} + +/** + * Create a single colored div with the given hue/saturation/luminosity. + * This will spin indefinitely, due to css. + */ +function createLine( + hue: number, + saturation: number, + luminosity: number, + lineNumber: number +): ReactElement { + const randomColor = `hsl(${hue}, ${saturation * 100}%, ${luminosity * 100}%)`; + const top = lineNumber % 100; + const left = (lineNumber % 110) - 20; + const randomStyle = { + backgroundColor: randomColor, + color: randomColor, + top: `${top}%`, + left: `${left}%`, + }; + return ( +
reverseAnimation(e)} + >
+ ); +} + +/** + * Draw a bunch of lines in a div that rotate and create a sort of shimmer + * effect. + */ +export default function PrettyColors({ baseColor }: Props) { + const hslColor = hexToHsl(baseColor); + // Draw 1000 lines (10^3). + const coloredLines = createLines(hslColor, 10); + + return ( + <> +
{coloredLines}
+ + ); +} diff --git a/addons-web-sdk/samples/animation-next-js/src/shared/constants.ts b/addons-web-sdk/samples/animation-next-js/src/shared/constants.ts new file mode 100644 index 0000000..f31396e --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/src/shared/constants.ts @@ -0,0 +1,44 @@ +// Copyright 2024 Google LLC +// +// Licensed 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * Identifier for the Cloud Project that is used to configure this add-on's + * manifest and Google Workspace Marketplace listing. + * @see {@link https://cloud.google.com/resource-manager/docs/creating-managing-projects#identifying_projects} + */ +export const CLOUD_PROJECT_NUMBER = '509165100245'; + +export const SITE_BASE = inDebugMode() + ? 'https://localhost:3000/meet/animation-next-js' + : 'https://googleworkspace.github.io/meet/animation-next-js'; + +function inDebugMode() { + return process.env.NEXT_PUBLIC_DEBUG === '1'; +} + +/** + * @see {@link https://developers.google.com/meet/add-ons/guides/overview#main-stage} + */ +export const MAIN_STAGE_URL = SITE_BASE + '/mainstage'; +/** + * The page that displays in the Side Panel for the activity initiator to set + * the activity starting state. + * @see {@link https://developers.google.com/meet/add-ons/guides/overview#side-panel} + */ +export const SIDE_PANEL_URL = SITE_BASE + '/sidepanel'; +/** + * The page that displays in the Side Panel for all participants to toggle settings. + * @see {@link https://developers.google.com/meet/add-ons/guides/overview#side-panel} + */ +export const ACTIVITY_SIDE_PANEL_URL = SITE_BASE + '/activitysidepanel'; diff --git a/addons-web-sdk/samples/animation-next-js/tsconfig.json b/addons-web-sdk/samples/animation-next-js/tsconfig.json new file mode 100644 index 0000000..ab72440 --- /dev/null +++ b/addons-web-sdk/samples/animation-next-js/tsconfig.json @@ -0,0 +1,43 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": [ + "./src/*" + ] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + "build/types/**/*.ts", + "../dist/game-next-js/types/**/*.ts", + "firebasedist/types/**/*.ts", + "../dist/animation-next-js/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/addons-web-sdk/samples/dist/privacy.html b/addons-web-sdk/samples/dist/privacy.html new file mode 100644 index 0000000..cffd222 --- /dev/null +++ b/addons-web-sdk/samples/dist/privacy.html @@ -0,0 +1,3 @@ + diff --git a/addons-web-sdk/samples/dist/support.html b/addons-web-sdk/samples/dist/support.html new file mode 100644 index 0000000..927e167 --- /dev/null +++ b/addons-web-sdk/samples/dist/support.html @@ -0,0 +1,3 @@ + diff --git a/addons-web-sdk/samples/dist/terms.html b/addons-web-sdk/samples/dist/terms.html new file mode 100644 index 0000000..7f2370e --- /dev/null +++ b/addons-web-sdk/samples/dist/terms.html @@ -0,0 +1,3 @@ + diff --git a/addons-web-sdk/samples/hello-world-next-js/.gitignore b/addons-web-sdk/samples/hello-world-next-js/.gitignore index c9262b8..64331da 100644 --- a/addons-web-sdk/samples/hello-world-next-js/.gitignore +++ b/addons-web-sdk/samples/hello-world-next-js/.gitignore @@ -1,4 +1,3 @@ # next.js -/.next/ -next-env.d.ts +/.next out diff --git a/addons-web-sdk/samples/hello-world-next-js/README.md b/addons-web-sdk/samples/hello-world-next-js/README.md index 4e8cc11..065e6e9 100644 --- a/addons-web-sdk/samples/hello-world-next-js/README.md +++ b/addons-web-sdk/samples/hello-world-next-js/README.md @@ -1,5 +1,5 @@ # React TypeScript example -This "hello, world!" add-on is built using Next JS to show how an Add-on can be deployed as a static site using React and TypeScript. It is very similar to the sample at , but the basic structure is a multi-page React app using NextJS's app routing. Please see other samples at to learn more advanced functionality! +This "hello, world!" add-on is built using [Next.js](https://nextjs.org/) to show how an Add-on can be deployed as a static site using React and TypeScript. It is very similar to the sample at [googleworkspace/meet/addons-web-sdk/samples/hello-world](https://github.com/googleworkspace/meet/tree/main/addons-web-sdk/samples/hello-world), but the basic structure is a multi-page React app using Next.js's app routing. Please see our other [samples](https://github.com/googleworkspace/meet/tree/main/addons-web-sdk/samples) to learn more advanced functionality! -You can view a deployed version of this Add-on at and its corresponding [mainstage](https://googleworkspace.github.io/meet/hello-world-next-js/mainstage). +This add-on is deployed with GitHub pages, so that you can view the live versions of its [Side Panel](https://googleworkspace.github.io/meet/hello-world-next-js/sidepanel) and [Main Stage](https://googleworkspace.github.io/meet/hello-world-next-js/mainstage). diff --git a/addons-web-sdk/samples/hello-world-next-js/next-env.d.ts b/addons-web-sdk/samples/hello-world-next-js/next-env.d.ts new file mode 100644 index 0000000..d62b474 --- /dev/null +++ b/addons-web-sdk/samples/hello-world-next-js/next-env.d.ts @@ -0,0 +1,19 @@ +// Copyright 2024 Google LLC +// +// Licensed 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 CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/addons-web-sdk/samples/hello-world-next-js/src/app/layout.tsx b/addons-web-sdk/samples/hello-world-next-js/src/app/layout.tsx index a14e64f..60bf659 100644 --- a/addons-web-sdk/samples/hello-world-next-js/src/app/layout.tsx +++ b/addons-web-sdk/samples/hello-world-next-js/src/app/layout.tsx @@ -1,16 +1,18 @@ -export const metadata = { - title: 'Next.js', - description: 'Generated by Next.js', -} +import { type Metadata } from 'next'; + +export const metadata: Metadata = { + title: 'Hello World!', + description: 'Google Meet Add-on build in Next.js', +}; export default function RootLayout({ children, }: { - children: React.ReactNode + children: React.ReactNode; }) { return ( {children} - ) + ); } diff --git a/addons-web-sdk/samples/package-lock.json b/addons-web-sdk/samples/package-lock.json index 9666b2d..9dafcda 100644 --- a/addons-web-sdk/samples/package-lock.json +++ b/addons-web-sdk/samples/package-lock.json @@ -7,15 +7,15 @@ "name": "meet-add-on-samples", "license": "Apache-2.0", "workspaces": [ + "animation-next-js", "hello-world", "hello-world-next-js" ] }, - "game-next-js": { + "animation-next-js": { "version": "0.1.0", - "extraneous": true, "dependencies": { - "@googleworkspace/meet-addons": "^0.9.1-668624766", + "@googleworkspace/meet-addons": "^0.12.0", "next": "14.2.7", "react": "^18", "react-dom": "^18" @@ -27,6 +27,9 @@ "typescript": "^5" } }, + "animation-next-js/@googleworkspace/meet-addons@next": { + "extraneous": true + }, "hello-world": { "license": "Apache-2.0", "dependencies": { @@ -52,6 +55,18 @@ "typescript": "^5" } }, + "hello-world-next-js/node_modules/@googleworkspace/meet-addons": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@googleworkspace/meet-addons/-/meet-addons-0.9.1.tgz", + "integrity": "sha512-GBHec8Cau90Edyi88YEWxvdfwm/0Aa1B/1HQbISjCh4t3sybGY/5Da9ReHMid3th0e2HtzPWpiCvROIQOS37dw==", + "license": "SEE LICENSE IN LICENSE" + }, + "hello-world/node_modules/@googleworkspace/meet-addons": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@googleworkspace/meet-addons/-/meet-addons-0.9.1.tgz", + "integrity": "sha512-GBHec8Cau90Edyi88YEWxvdfwm/0Aa1B/1HQbISjCh4t3sybGY/5Da9ReHMid3th0e2HtzPWpiCvROIQOS37dw==", + "license": "SEE LICENSE IN LICENSE" + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", @@ -63,9 +78,9 @@ } }, "node_modules/@googleworkspace/meet-addons": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@googleworkspace/meet-addons/-/meet-addons-0.9.1.tgz", - "integrity": "sha512-GBHec8Cau90Edyi88YEWxvdfwm/0Aa1B/1HQbISjCh4t3sybGY/5Da9ReHMid3th0e2HtzPWpiCvROIQOS37dw==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@googleworkspace/meet-addons/-/meet-addons-0.12.0.tgz", + "integrity": "sha512-RO3f+lkq9JUDxR5jac4hX0O+90RGlAe2FUI5aQSeYLY0f9ViUuhhQhB4yoLZ33Q8brXVo6dULK6BaX0qmqsz1Q==", "license": "SEE LICENSE IN LICENSE" }, "node_modules/@jridgewell/gen-mapping": { @@ -302,7 +317,8 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -312,9 +328,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.16.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.2.tgz", - "integrity": "sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==", + "version": "20.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", + "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==", "dev": true, "license": "MIT", "dependencies": { @@ -329,9 +345,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.4", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz", - "integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==", + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", + "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", "dev": true, "license": "MIT", "dependencies": { @@ -621,6 +637,10 @@ "ajv": "^6.9.1" } }, + "node_modules/animation-next-js": { + "resolved": "animation-next-js", + "link": true + }, "node_modules/browserslist": { "version": "4.23.3", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", @@ -673,9 +693,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001653", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz", - "integrity": "sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw==", + "version": "1.0.30001657", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001657.tgz", + "integrity": "sha512-DPbJAlP8/BAXy3IgiWmZKItubb3TYGP0WscQQlVGIfT4s/YlFYVuJgyOsQNP7rJRChx/qdMeLJQJP0Sgg2yjNA==", "funding": [ { "type": "opencollective", @@ -760,9 +780,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==", + "version": "1.5.14", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.14.tgz", + "integrity": "sha512-bEfPECb3fJ15eaDnu9LEJ2vPGD6W1vt7vZleSVyFhYuMIKm3vz/g9lt7IvEzgdwj58RjbPKUF2rXTCN/UW47tQ==", "dev": true, "license": "ISC" }, @@ -801,9 +821,9 @@ "license": "MIT" }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "license": "MIT", "engines": { @@ -1304,9 +1324,9 @@ "license": "MIT" }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "license": "ISC" }, "node_modules/pkg-dir": { @@ -1785,6 +1805,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", diff --git a/addons-web-sdk/samples/package.json b/addons-web-sdk/samples/package.json index 1e77477..7aba229 100644 --- a/addons-web-sdk/samples/package.json +++ b/addons-web-sdk/samples/package.json @@ -9,6 +9,7 @@ "build": "npm install && npm run build --workspaces" }, "workspaces": [ + "animation-next-js", "hello-world", "hello-world-next-js" ]