I'm trying to create a very simple Powerpoint Add-in to learn how to do so. The add-in is a Powerpoint taskpane that is supposed to write "Hello world" in the selected text frame. I have a Python Flask app and a simple taskpane.html that I took from the official samples. I also took their manifest-localhost.xml. I just replaced the 3000 port to 5000 and the ID with a GUID that I generated.
The manifest loads well and I do find a task pane named Hello World. But nothing loads in it.
Initially I get:
"""
Add-in Error Something went wrong and we couldn't start this add-in. Please try again later or contact your system administrator.
"""
When I retry I get:
"""
Add-in Error This add-in may not load properly, but you can still try to start it.
"""
And when I click on start nothing happens. The task pane stays empty.
The javascript code that is in taskpane.html in the Script Lab add-in works well. The flask app runs correctly and the endpoints all return a 200 status code.
My project structure is:
.
├── app.py
├── assets
│ ├── favicon.ico
│ ├── icon-128.png
│ ├── icon-16.png
│ ├── icon-32.png
│ ├── icon-64.png
│ └── icon-80.png
├── certs
│ ├── localhost+2-key.pem
│ └── localhost+2.pem
├── manifest-localhost.xml
├── README.md
└── templates
└── taskpane.html
I have generated localhost certificates using mkcert. I have authorised localhost certificates in my browser (Brave), and when going to https://127.0.0.1:5000 it shows that it is secure. I have also checked it using curl -v https://localhost:5000 —cacert ./certs/localhost+2.pem.
The app.py's code:
from flask import Flask, render_template
from flask.helpers import send_file
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route('/')
def home():
return "Hello World!"
@app.route("/assets/icon-16.png")
def icon16():
return send_file("./assets/icon-16.png")
@app.route("/assets/icon-32.png")
def icon32():
return send_file("./assets/icon-32.png")
@app.route("/assets/icon-64.png")
def icon64():
return send_file("./assets/icon-64.png")
@app.route("/assets/icon-80.png")
def icon80():
return send_file("./assets/icon-80.png")
@app.route("/assets/icon-128.png")
def icon128():
return send_file("./assets/icon-128.png")
@app.route('/favicon.ico')
def favicon():
return send_file('./assets/favicon.ico')
@app.route("/taskpane.html")
def taskpane():
return render_template("taskpane.html")
if __name__ == "__main__":
ssl_context = ("./certs/localhost+2.pem", "./certs/localhost+2-key.pem")
app.run(debug=True, ssl_context=ssl_context)
The taskpane.html:
<!DOCTYPE html>
<html>
<head>
<!-- Office JavaScript API -->
<script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>
</head>
<body>
<p>This add-in will insert 'Hello world!' in the current selection of the slide.</p>
<button onclick="sayHello()">Say hello</button>
</body>
<script>
Office.onReady((info) => {
document.getElementById("helloButton").onclick = sayHello;
});
async function sayHello() {
// Set coercion type to text since
const options = { coercionType: Office.CoercionType.Text };
// clear current selection
await Office.context.document.setSelectedDataAsync(" ", options);
// Set text in selection to 'Hello world!'
await Office.context.document.setSelectedDataAsync("Hello world!", options);
}
</script>
</html>
I can also include the manifest file if needed but I don't want to make this longer than needed.
Thank you in advance!