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
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,14 @@ jobs:
- name: Setup Helm
uses: azure/setup-helm@v5

- name: Generate deployment YAML
- name: Generate deployment YAML and landing page
run: |
mkdir public
helm template console-functions-plugin charts/openshift-console-plugin \
-n console-functions-plugin \
--set "plugin.image=${{ env.IMAGE }}@${{ needs.publish.outputs.digest }}" \
> public/plugin.yaml
cp pages/index.html public/index.html

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v5
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ install-state.gz

# Built binaries
/bin
pages/plugin.yaml
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ A Functions-as-a-Service PoC UI for the OpenShift Web Console. Developers create

Built as an [OpenShift Console dynamic plugin](https://github.com/openshift/console/tree/main/frontend/packages/console-dynamic-plugin-sdk) using React, TypeScript, and PatternFly 6.

**[Deploy to your cluster](https://twogiants.github.io/func-console/)**

## Deployment on cluster

### Prerequisites
Expand Down
248 changes: 248 additions & 0 deletions pages/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Serverless Functions Console Plugin</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&family=Sora:wght@400;600;700&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/github-dark.min.css" rel="stylesheet" integrity="sha384-wH75j6z1lH97ZOpMOInqhgKzFkAInZPPSPlZpYKYTOqsaizPvhQZmAtLcPKXpLyH" crossorigin="anonymous">
<style>
:root {
--accent: #e86a2a;
--accent-soft: rgba(232, 106, 42, 0.12);
--surface: #161b22;
--border-subtle: rgba(255, 255, 255, 0.06);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Sora', sans-serif;
background: #0d1117;
color: #c9d1d9;
min-height: 100vh;
}
.container {
max-width: 680px;
margin: 0 auto;
padding: 0 1.5rem;
}
.hero {
padding: 5rem 0 1rem;
text-align: center;
position: relative;
}
.hero::before {
content: '';
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 600px;
height: 400px;
background: radial-gradient(ellipse, var(--accent-soft) 0%, transparent 70%);
pointer-events: none;
}
.hero h1 {
font-weight: 700;
font-size: 2.4rem;
letter-spacing: -0.03em;
line-height: 1.2;
color: #fff;
}
.hero .accent { color: var(--accent); }
.hero p {
color: #8b949e;
font-size: 1.05rem;
max-width: 480px;
margin: 0.75rem auto 0;
}
.badge-version {
display: inline-block;
font-family: 'JetBrains Mono', monospace;
font-size: 0.7rem;
background: var(--accent-soft);
color: var(--accent);
padding: 0.25rem 0.6rem;
border-radius: 20px;
font-weight: 500;
margin-bottom: 0.75rem;
}
.card {
background: var(--surface);
border: 1px solid var(--border-subtle);
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 0.75rem;
transition: border-color 0.2s;
}
.card:hover { border-color: rgba(255, 255, 255, 0.1); }
.card-title {
font-weight: 600;
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #8b949e;
margin-bottom: 1rem;
}
pre {
font-family: 'JetBrains Mono', monospace;
font-size: 0.82rem;
line-height: 1.7;
background: #0d1117;
border: 1px solid var(--border-subtle);
border-radius: 8px;
padding: 1.2rem;
margin: 0;
overflow-x: auto;
}
.prompt {
color: var(--accent);
user-select: none;
}
.btn-row {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.btn {
display: inline-block;
font-family: 'Sora', sans-serif;
font-weight: 600;
font-size: 0.82rem;
letter-spacing: 0.02em;
padding: 0.5rem 1.2rem;
border-radius: 8px;
text-decoration: none;
transition: all 0.2s;
cursor: pointer;
}
.btn-accent {
background: var(--accent);
color: #fff;
border: none;
}
.btn-accent:hover {
background: #d45d22;
transform: translateY(-1px);
}
.btn-ghost {
background: transparent;
color: #8b949e;
border: 1px solid var(--border-subtle);
}
.btn-ghost:hover {
color: #c9d1d9;
border-color: rgba(255, 255, 255, 0.15);
background: #1c2129;
}
.yaml-scroll {
max-height: 420px;
overflow-y: auto;
border-radius: 8px;
}
.yaml-scroll::-webkit-scrollbar { width: 6px; }
.yaml-scroll::-webkit-scrollbar-track { background: transparent; }
.yaml-scroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 3px; }
.yaml-scroll::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.2); }
.last-card { margin-bottom: 3rem; }
.code-wrap { position: relative; }
.copy-btn {
position: absolute;
top: 0.6rem;
right: 0.6rem;
background: transparent;
border: none;
color: #484f58;
cursor: pointer;
padding: 0.3rem;
border-radius: 4px;
transition: all 0.2s;
line-height: 1;
}
.copy-btn:hover { color: #8b949e; background: rgba(255, 255, 255, 0.06); }
.copy-btn.copied { color: var(--accent); }
.copy-icon { width: 16px; height: 16px; }
.check-icon { width: 16px; height: 16px; display: none; }
.copy-btn.copied .copy-icon { display: none; }
.copy-btn.copied .check-icon { display: inline; }
.fade-in { animation: fadeIn 0.6s ease both; }
.fade-in-delay-1 { animation-delay: 0.1s; }
.fade-in-delay-2 { animation-delay: 0.2s; }
.fade-in-delay-3 { animation-delay: 0.3s; }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body>
<div class="container">
<div class="hero fade-in">
<span class="badge-version">OpenShift Console Plugin</span>
<h1>Serverless Functions<br><span class="accent">Console Plugin</span></h1>
<p>Create, edit, and deploy serverless functions from the OpenShift Web Console.</p>
</div>

<div class="card fade-in fade-in-delay-1">
<h5 class="card-title">Install</h5>
<div class="code-wrap">
<button class="copy-btn" data-target="install-cmd" data-strip="$ ">
<svg class="copy-icon" viewBox="0 0 16 16" fill="currentColor"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg>
<svg class="check-icon" viewBox="0 0 16 16" fill="currentColor"><path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"/></svg>
</button>
<pre><code id="install-cmd"><span class="prompt">$</span> oc new-project console-functions-plugin
<span class="prompt">$</span> oc apply -f https://twogiants.github.io/func-console/plugin.yaml</code></pre>
</div>
</div>

<div class="card fade-in fade-in-delay-2">
<h5 class="card-title">Resources</h5>
<div class="btn-row">
<a href="plugin.yaml" class="btn btn-accent">plugin.yaml</a>
<a href="https://github.com/twoGiants/func-console" class="btn btn-ghost">Repository</a>
<a href="https://github.com/twoGiants/func-console/pkgs/container/console-functions-plugin" class="btn btn-ghost">Container Registry</a>
</div>
</div>

<div class="card last-card fade-in fade-in-delay-3">
<h5 class="card-title">plugin.yaml</h5>
<div class="code-wrap">
<button class="copy-btn" data-target="yaml-content">
<svg class="copy-icon" viewBox="0 0 16 16" fill="currentColor"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"/><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"/></svg>
<svg class="check-icon" viewBox="0 0 16 16" fill="currentColor"><path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"/></svg>
</button>
<div class="yaml-scroll">
<pre><code id="yaml-content">Loading...</code></pre>
</div>
</div>
</div>
</div>

<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js" integrity="sha384-RH2xi4eIQ/gjtbs9fUXM68sLSi99C7ZWBRX1vDrVv6GQXRibxXLbwO2NGZB74MbU" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/languages/yaml.min.js" integrity="sha384-A/iMReLA0Bo3tLydBIoOQXQzYnrwL90jkHYUubrtERUGCbIuU7U0EHge0Xd2s5sr" crossorigin="anonymous"></script>
<script>
document.querySelectorAll('.copy-btn').forEach(btn => {
btn.addEventListener('click', () => {
let text = document.getElementById(btn.dataset.target).textContent;
const strip = btn.dataset.strip;
if (strip) {
text = text.split('\n').map(l => l.startsWith(strip) ? l.slice(strip.length) : l).join('\n');
}
navigator.clipboard.writeText(text).then(() => {
btn.classList.add('copied');
setTimeout(() => { btn.classList.remove('copied'); }, 1500);
});
});
});

fetch('plugin.yaml')
.then(r => r.text())
.then(text => {
const el = document.getElementById('yaml-content');
el.textContent = text;
el.classList.add('language-yaml');
hljs.highlightElement(el);
})
.catch(() => { document.getElementById('yaml-content').textContent = 'Failed to load plugin.yaml'; });
</script>
</body>
</html>