Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publish Blueprints from the Gallery as a PWAs #88

Draft
wants to merge 6 commits into
base: trunk
Choose a base branch
from
Draft
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
4 changes: 4 additions & 0 deletions .github/workflows/postprocess.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ jobs:
run: npm install -g prettier
- name: Run reindex_postprocess.py
run: python reindex_postprocess.py
- name: Run PWA build
run: python build_pwa.py
env:
PWA_HOME_PATH: /blueprints/
- name: Run Prettier
run: prettier --write blueprints/**/*.json
- name: Check for uncommitted changes
Expand Down
67 changes: 67 additions & 0 deletions build_pwa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import os
import json
import shutil

# Define the base directory where blueprints are located
BLUEPRINTS_DIR = 'blueprints'
TEMPLATE_DIR = 'pwa-template'
PWA_HOME_PATH = os.getenv('PWA_HOME_PATH', '/')

def read_template(file_path):
with open(file_path, 'r') as file:
return file.read()

def build_pwa_for_folder(folder_path):
# Read blueprint.json
blueprint_path = os.path.join(folder_path, 'blueprint.json')
if not os.path.exists(blueprint_path):
print(f"Skipping {folder_path}, no blueprint.json found.")
return

with open(blueprint_path, 'r') as f:
blueprint = json.load(f)

# Extract necessary fields
title = blueprint['meta']['title']
description = blueprint['meta']['description']
start_url = PWA_HOME_PATH + folder_path + "/"
slug = folder_path.split('/')[-1]

# Read and format index.html template
index_html_template = read_template(os.path.join(TEMPLATE_DIR, 'index.html'))
index_html_content = index_html_template.replace('{{PWA_NAME}}', title)\
.replace('{{BLUEPRINT}}', json.dumps(blueprint, indent=2))\
.replace('{{PWA_SLUG}}', slug)

with open(os.path.join(folder_path, 'index.html'), 'w') as f:
f.write(index_html_content)

# Read and format manifest.json template
manifest_json_template = read_template(os.path.join(TEMPLATE_DIR, 'manifest.json'))
manifest_json_content = manifest_json_template.replace('{{PWA_NAME}}', title)\
.replace('{{PWA_SHORT_NAME}}', title.split()[0])\
.replace('{{PWA_DESCRIPTION}}', description)\
.replace('{{PWA_START_URL}}', start_url)

with open(os.path.join(folder_path, 'manifest.json'), 'w') as f:
f.write(manifest_json_content)

# Ensure icons directory exists
icons_dir = os.path.join(folder_path, 'icons')
if not os.path.exists(icons_dir):
os.makedirs(icons_dir)
# Copy default icons if they don't exist
default_icons_dir = os.path.join(TEMPLATE_DIR, 'icons')
for icon in os.listdir(default_icons_dir):
if not os.path.exists(os.path.join(icons_dir, icon)):
shutil.copy(os.path.join(default_icons_dir, icon), icons_dir)

def main():
# Traverse each folder in blueprints directory
for folder_name in os.listdir(BLUEPRINTS_DIR):
folder_path = os.path.join(BLUEPRINTS_DIR, folder_name)
if os.path.isdir(folder_path):
build_pwa_for_folder(folder_path)

if __name__ == "__main__":
main()
Binary file added pwa-template/icons/logo-192.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 pwa-template/icons/logo-256.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 pwa-template/icons/logo-384.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 pwa-template/icons/logo-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
136 changes: 136 additions & 0 deletions pwa-template/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{PWA_NAME}}</title>
<link rel="manifest" href="manifest.json" />
<style>
:root {
--header-height: 39px;
--header-padding: 8px;
}

body {
margin: 0;
padding: 0;
}

.install-header {
height: var(--header-height);
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--header-padding);
background-color: #1e2327;
}

.install-button {
color: #9ba2a6;
background: #2b2a33;
border: none;
padding: 8px 16px;
cursor: pointer;
border-radius: 4px;
}

.install-button:hover {
background: #9ba2a6;
color: #1e2327;
}

#closeInstall {
font-size: 24px;
line-height: 15px;
}

#wp {
width: 100%;
height: calc(100vh - var(--header-height) - (2 * var(--header-padding)));
border: 0;
}

.hide-install-header .install-header {
display: none;
}
.hide-install-header #wp {
height: 100vh;
}
</style>
</head>

<body class="hide-install-header">
<header class="install-header">
<button id="install" class="install-button">Install {{PWA_NAME}}</button>
<button id="closeInstall" class="install-button">×</button>
</header>
<iframe id="wp"></iframe>
<script type="module" async>
const slug = "{{PWA_SLUG}}";

/**
* Install PWA button
*/
let installPrompt = null;
const installButton = document.getElementById("install");
const closeInstallButton = document.getElementById("closeInstall");
const installHeader = document.querySelector(".install-header");
window.addEventListener("beforeinstallprompt", (event) => {
event.preventDefault();
installPrompt = event;
if (localStorage.getItem(`hide-install-header-for-${slug}`) === 'true') {
hideInstallHeader();
} else {
showInstallHeader();
}
});
installButton.addEventListener("click", async () => {
if (!installPrompt) {
return;
}
const result = await installPrompt.prompt();
installPrompt = null;
hideInstallHeader();
});
closeInstallButton.addEventListener("click", () => {
localStorage.setItem(`hide-install-header-for-${slug}`, 'true');
hideInstallHeader();
});
function hideInstallHeader() {
document.body.classList.add("hide-install-header");
}
function showInstallHeader() {
document.body.classList.remove("hide-install-header");
}

// Boot Playground
import { startPlaygroundWeb } from "https://playground.wordpress.net/client/index.js";

function isWordPressInstalled(slug) {
return localStorage.getItem(`wordpress-installed-for-${slug}`) === 'true';
}
function setWordPressInstalled(slug) {
localStorage.setItem(`wordpress-installed-for-${slug}`, 'true');
}
const shouldInstallWordPress = !isWordPressInstalled(slug);
const client = await startPlaygroundWeb({
iframe: document.getElementById("wp"),
remoteUrl: `https://playground.wordpress.net/remote.html`,
mounts: [
{
mountpoint: '/wordpress',
device: {
type: 'opfs',
path: `/pwa-sites/${slug}`,
},
},
],
shouldInstallWordPress,
blueprint: {{BLUEPRINT}}
});
await client.isReady();
if (shouldInstallWordPress) {
setWordPressInstalled(slug);
}
</script>
</body>
</html>
35 changes: 35 additions & 0 deletions pwa-template/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "{{PWA_NAME}}",
"short_name": "{{PWA_SHORT_NAME}}",
"description": "{{PWA_DESCRIPTION}}",
"start_url": "{{PWA_START_URL}}",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0073aa",
"categories": [
"rss",
"social web"
],
"icons": [
{
"src": "icons/logo-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/logo-256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "icons/logo-384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "icons/logo-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
Loading