Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ dist
dist-ssr
*.local
.env
.env.*
.npmrc
.eslintcache

Expand Down
2 changes: 2 additions & 0 deletions examples/word-addin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
auth0-config.js
server-domain.js
7 changes: 7 additions & 0 deletions examples/word-addin/MS-Word-Add-in-Sample.code-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"folders": [
{
"path": "."
}
]
}
165 changes: 165 additions & 0 deletions examples/word-addin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# SuperDoc MS Add-in Sync

Real-time document synchronization between Microsoft Word Add-in and web editor using WebSocket communication.

## Architecture

The system consists of three main components:
- **MS Word Add-in** (`src/taskpane/taskpane.js`) - Runs inside Microsoft Word
- **Web Editor** (`server/public/editor.js`) - Browser-based document editor
- **Node.js Server** (`server/server.js`) - WebSocket server handling real-time sync

## WebSocket Events

The WebSocket communication uses the following event types:

### `client_ready`
**Sent by:** Web Editor
**Handled by:** Server (broadcasts to other clients)
**Purpose:** Signals that a browser client has loaded and is ready to receive authentication

```javascript
// Sent by editor.js
websocket.send(JSON.stringify({
type: 'client_ready'
}));

// Broadcasted by server.js to other clients
{
type: 'client_ready',
timestamp: '2024-01-01T00:00:00.000Z'
}
```

### `token_transfer`
**Sent by:** MS Word Add-in
**Handled by:** Server (validates and broadcasts to other clients), Web Editor
**Purpose:** Transfers authentication token from Word add-in to web editor

```javascript
// Sent by taskpane.js
websocket.send(JSON.stringify({
type: 'token_transfer',
token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik...'
}));

// Broadcasted by server.js after validation
{
type: 'token_transfer',
token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik...',
user: {
email: 'user@example.com',
name: 'User Name',
picture: 'https://example.com/avatar.jpg'
},
timestamp: '2024-01-01T00:00:00.000Z'
}

// Error response
{
type: 'token_transfer',
error: 'Invalid token',
timestamp: '2024-01-01T00:00:00.000Z'
}
```

### `document_update`
**Sent by:** MS Word Add-in, Web Editor
**Handled by:** Server (validates and broadcasts to other clients), MS Word Add-in, Web Editor
**Purpose:** Real-time document synchronization between clients

```javascript
// Sent by taskpane.js or editor.js
websocket.send(JSON.stringify({
type: 'document_update',
token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik...',
document: 'UEsDBBQABgAIAAAAIQDb4fbL7...' // Base64 encoded .docx
}));

// Broadcasted by server.js after validation
{
type: 'document_update',
document: 'UEsDBBQABgAIAAAAIQDb4fbL7...',
author: 'user@example.com',
timestamp: '2024-01-01T00:00:00.000Z'
}
```

### `close`
**Sent by:** Server
**Handled by:** MS Word Add-in, Web Editor
**Purpose:** Notifies clients when another connection closes

```javascript
// Broadcasted by server.js
{
type: 'close',
timestamp: '2024-01-01T00:00:00.000Z'
}
```

### `error`
**Sent by:** Server
**Handled by:** MS Word Add-in, Web Editor
**Purpose:** Notifies clients when a connection error occurs

```javascript
// Broadcasted by server.js
{
type: 'error',
timestamp: '2024-01-01T00:00:00.000Z'
}
```

## Authentication Flow

1. Web editor loads and sends `client_ready` to server
2. Server broadcasts `client_ready` to Word add-in
3. Word add-in sends `token_transfer` with user's authentication token
4. Server validates token against Auth0 and broadcasts `token_transfer` with user info to web editor
5. Both clients are now authenticated and can send `document_update` events

## Real-time Synchronization

- Document changes in Word trigger `document_update` events via selection change detection
- Document changes in web editor trigger `document_update` events via SuperDoc's `onEditorUpdate` callback
- All `document_update` events include the full document as Base64-encoded .docx
- Server validates authentication token before broadcasting updates
- Clients update their document content when receiving `document_update` events

## Setup

1. Install dependencies:
```bash
npm install
cd server && npm install
```

2. Configure environment variables:

**Auth0 Configuration** - Set up `src/auth0-config.js`:
- Get your Auth0 domain, client ID, and audience from your [Auth0 Dashboard](https://manage.auth0.com/)
- Create a new application or use an existing Single Page Application
- Configure the redirect URLs to include your add-in's domain

**Server Configuration** - Set up `server/.env`:
- `AUTH0_DOMAIN`: Your Auth0 domain (e.g., `yourapp.us.auth0.com`)
- `AUTH0_AUDIENCE`: Your Auth0 API identifier
- Any additional environment variables required by your cloud function

These environment variables are required for:
- **Auth0**: Authenticating users and validating JWT tokens
- **Cloud Function**: Server-side token validation and WebSocket communication

You can reference the example files in the same directories.

3. Start the server:
```bash
npm run server
```

4. Build and run the add-in:
```bash
npm run build
npm start
```
Binary file added examples/word-addin/assets/icon-128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/word-addin/assets/icon-128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/word-addin/assets/icon-16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/word-addin/assets/icon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/word-addin/assets/icon-32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/word-addin/assets/icon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/word-addin/assets/icon-64.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/word-addin/assets/icon-64x64.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/word-addin/assets/icon-80.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/word-addin/assets/icon-80x80.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/word-addin/assets/logo-filled.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/word-addin/assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
12 changes: 12 additions & 0 deletions examples/word-addin/babel.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"esmodules": false
}
}
],
]
}
8 changes: 8 additions & 0 deletions examples/word-addin/demo-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tags": [
"editing",
"viewing"
],
"title": "Word Add-in",
"type": "video"
}
Binary file added examples/word-addin/demo-thumbnail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/word-addin/demo-video.mp4
Binary file not shown.
79 changes: 79 additions & 0 deletions examples/word-addin/manifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OfficeApp xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="TaskPaneApp">
<Id>905fc5c6-e25e-438e-a189-b20b7615ad23</Id>
<Version>1.0.0.1</Version>
<ProviderName>SuperDoc</ProviderName>
<DefaultLocale>en-US</DefaultLocale>
<DisplayName DefaultValue="SuperDoc - Word Add-in"/>
<Description DefaultValue="Moden document editing"/>
<IconUrl DefaultValue="https://localhost:3000/assets/icon-32x32.png"/>
<HighResolutionIconUrl DefaultValue="https://localhost:3000/assets/icon-64x64.png"/>
<SupportUrl DefaultValue="https://www.harbourshare.com/support"/>

<Hosts>
<Host Name="Document"/>
</Hosts>
<DefaultSettings>
<SourceLocation DefaultValue="https://localhost:3000/taskpane.html"/>
</DefaultSettings>
<Permissions>ReadWriteDocument</Permissions>
<VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0">
<Hosts>
<Host xsi:type="Document">
<DesktopFormFactor>
<GetStarted>
<Title resid="GetStarted.Title"/>
<Description resid="GetStarted.Description"/>
<LearnMoreUrl resid="GetStarted.LearnMoreUrl"/>
</GetStarted>
<ExtensionPoint xsi:type="PrimaryCommandSurface">
<OfficeTab id="TabHome">
<Group id="CommandsGroup">
<Label resid="CommandsGroup.Label"/>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Control xsi:type="Button" id="TaskpaneButton">
<Label resid="TaskpaneButton.Label"/>
<Supertip>
<Title resid="TaskpaneButton.Label"/>
<Description resid="TaskpaneButton.Tooltip"/>
</Supertip>
<Icon>
<bt:Image size="16" resid="Icon.16x16"/>
<bt:Image size="32" resid="Icon.32x32"/>
<bt:Image size="80" resid="Icon.80x80"/>
</Icon>
<Action xsi:type="ShowTaskpane">
<TaskpaneId>ButtonId1</TaskpaneId>
<SourceLocation resid="Taskpane.Url"/>
</Action>
</Control>
</Group>
</OfficeTab>
</ExtensionPoint>
</DesktopFormFactor>
</Host>
</Hosts>
<Resources>
<bt:Images>
<bt:Image id="Icon.16x16" DefaultValue="https://localhost:3000/assets/icon-16x16.png"/>
<bt:Image id="Icon.32x32" DefaultValue="https://localhost:3000/assets/icon-32x32.png"/>
<bt:Image id="Icon.80x80" DefaultValue="https://localhost:3000/assets/icon-80x80.png"/>
</bt:Images>
<bt:Urls>
<bt:Url id="GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812"/>
<bt:Url id="Taskpane.Url" DefaultValue="https://localhost:3000/taskpane.html"/>
</bt:Urls>
<bt:ShortStrings>
<bt:String id="CommandsGroup.Label" DefaultValue="Commands Group"/>
<bt:String id="TaskpaneButton.Label" DefaultValue="SuperDoc"/>
</bt:ShortStrings>
<bt:LongStrings>
<bt:String id="TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane"/>
</bt:LongStrings>
</Resources>
</VersionOverrides>
</OfficeApp>
70 changes: 70 additions & 0 deletions examples/word-addin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"name": "office-addin-taskpane-js2",
"version": "0.0.1",
"repository": {
"type": "git",
"url": "https://github.com/OfficeDev/Office-Addin-TaskPane-JS.git"
},
"license": "MIT",
"config": {
"app_to_debug": "word",
"app_type_to_debug": "desktop",
"dev_server_port": 3000
},
"scripts": {
"build": "webpack --mode production",
"build:dev": "webpack --mode development",
"dev-server": "webpack serve --mode development",
"lint": "office-addin-lint check",
"lint:fix": "office-addin-lint fix",
"prettier": "office-addin-lint prettier",
"signin": "office-addin-dev-settings m365-account login",
"signout": "office-addin-dev-settings m365-account logout",
"start": "office-addin-debugging start manifest.xml",
"stop": "office-addin-debugging stop manifest.xml",
"validate": "office-addin-manifest validate manifest.xml",
"watch": "webpack --mode development --watch"
},
"dependencies": {
"@google-cloud/storage": "^7.13.0",
"auth0-js": "^9.28.0",
"core-js": "^3.36.0",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^5.1.0",
"express-oauth2-jwt-bearer": "^1.6.0",
"jsdom": "^26.1.0",
"regenerator-runtime": "^0.14.1",
"ws": "^8.18.0"
},
"devDependencies": {
"@babel/core": "^7.24.0",
"@babel/preset-env": "^7.25.4",
"@types/office-js": "^1.0.377",
"@types/office-runtime": "^1.0.35",
"acorn": "^8.11.3",
"babel-loader": "^9.1.3",
"copy-webpack-plugin": "^12.0.2",
"eslint-plugin-office-addins": "^3.0.2",
"file-loader": "^6.2.0",
"html-loader": "^5.0.0",
"html-webpack-plugin": "^5.6.3",
"office-addin-cli": "^1.6.5",
"office-addin-debugging": "^5.1.6",
"office-addin-dev-certs": "^1.13.5",
"office-addin-lint": "^2.3.5",
"office-addin-manifest": "^1.13.6",
"office-addin-prettier-config": "^1.2.1",
"os-browserify": "^0.3.0",
"process": "^0.11.10",
"source-map-loader": "^5.0.0",
"webpack": "^5.95.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "5.1.0"
},
"prettier": "office-addin-prettier-config",
"browserslist": [
"last 2 versions",
"ie 11"
]
}
11 changes: 11 additions & 0 deletions examples/word-addin/server/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Google Cloud Storage Configuration
GOOGLE_CLOUD_PROJECT_ID=
GOOGLE_APPLICATION_CREDENTIALS=
GCP_BUCKET_NAME=

# Auth0 Configuration
AUTH0_DOMAIN=
AUTH0_AUDIENCE=

# Server Configuration
PORT=8080
Loading
Loading