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"
]