diff --git a/website/app.js b/website/app.js
new file mode 100644
index 0000000..4c2dc0f
--- /dev/null
+++ b/website/app.js
@@ -0,0 +1,104 @@
+(function () {
+ function escapeHtml(str) {
+ return String(str)
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+ }
+
+ function chunk(arr, size) {
+ var out = [];
+ for (var i = 0; i < arr.length; i += size) out.push(arr.slice(i, i + size));
+ return out;
+ }
+
+ function renderLinksTable(links, columns) {
+ columns = columns || 3;
+ links = Array.isArray(links) ? links : [];
+
+ var rows = chunk(links, columns);
+ if (rows.length) {
+ var last = rows[rows.length - 1];
+ while (last.length < columns) last.push(null);
+ }
+
+ var html = "
";
+ for (var r = 0; r < rows.length; r++) {
+ html += "";
+ for (var c = 0; c < columns; c++) {
+ var item = rows[r][c];
+ if (!item) {
+ html += " | ";
+ continue;
+ }
+
+ var href = escapeHtml(item.href || "#");
+ var label = escapeHtml(item.label || "");
+ var img = escapeHtml(item.image || "");
+ var alt = escapeHtml(item.alt || item.label || "");
+
+ html +=
+ '' +
+ '' +
+ ' ' +
+ "" + label + " " +
+ "" +
+ " | ";
+ }
+ html += "
";
+ }
+ html += "
";
+ return html;
+ }
+
+ function renderAllSections(containerId, sectionsObj) {
+ var container = document.getElementById(containerId);
+ if (!container) return;
+
+ sectionsObj = sectionsObj || {};
+ var keys = Object.keys(sectionsObj);
+
+ // If you want a custom order, add an "order" number in links.js and sort here.
+ // Default: object insertion order.
+ var html = "";
+
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ var section = sectionsObj[key];
+
+ // support BOTH formats:
+ // 1) new format: { title, links: [...] }
+ // 2) old format: key: [ ...links... ]
+ var title, links;
+ if (Array.isArray(section)) {
+ title = key; // fallback title
+ links = section;
+ } else {
+ title = section && section.title ? section.title : key;
+ links = section && Array.isArray(section.links) ? section.links : [];
+ }
+
+ // Skip empty sections (optional)
+ if (!links.length) continue;
+
+ html += "" + escapeHtml(title) + "
";
+ html += renderLinksTable(links, 3);
+
+ // divider between sections
+ if (i !== keys.length - 1) html += "
";
+ }
+
+ container.innerHTML = html || "No sections defined.
";
+ }
+
+ window.addEventListener("DOMContentLoaded", function () {
+ // focus search input
+ var q = document.getElementById("q");
+ if (q) q.focus();
+
+ renderAllSections("sections", window.LAUNCHPAD_LINKS);
+ });
+ })();
+
\ No newline at end of file
diff --git a/website/index.html b/website/index.html
new file mode 100644
index 0000000..9fbe8f0
--- /dev/null
+++ b/website/index.html
@@ -0,0 +1,67 @@
+
+
+
+
+ SumiLaunch
+
+
+
+
+
+
+
+
SumiLaunch
+
+
+
+
+
+
+
+
+
+
+
TIP: Launchpad Shortcut
+

+
Designed for classic BBOS.
+
© 2026 Sumisu
+
Based on LunarProject.org.
+
+
+
+
+
+
diff --git a/website/links.js b/website/links.js
new file mode 100644
index 0000000..8f4a12b
--- /dev/null
+++ b/website/links.js
@@ -0,0 +1,37 @@
+window.LAUNCHPAD_LINKS = {
+ anyBrowser: {
+ title: "Any Browser",
+ links: [
+ { href: "./news/", label: "SumiNews", image: "http://192.168.1.242:8087/images/lunarnews.png", alt: "SumiNews" },
+ { href: "./weather/", label: "SumiWeather", image: "http://192.168.1.242:8087/images/weather.png", alt: "SumiWeather" },
+
+ { href: "https://pb-appstore.netlify.app/", label: "PB - Appstore", image: "https://cdn.bio.link/uploads/profile_pictures/2025-11-19/2lWy5sAQy0Ox3AMMehIBSTWkIzb6vcvi.png", alt: "PB - Appstore" },
+ { href: "http://news.ycombinator.com", label: "Hacker News", image: "http://192.168.1.242:8087/images/newsz.png", alt: "Hacker News" },
+
+ { href: "http://text.npr.org/", label: "NPR", image: "http://192.168.1.242:8087/images/npr.jpg", alt: "NPR" },
+
+ { href: "http://williamsmobile.co.uk/apps.htm", label: "WM Apps", image: "http://192.168.1.242:8087/images/wm.jpg", alt: "WilliamsMobile" },
+ { href: "http://gdir.telae.net/", label: "Gmaps", image: "http://192.168.1.242:8087/images/gmaps.png", alt: "Gmaps" },
+ { href: "http://old.reddit.com/", label: "Old Reddit", image: "http://192.168.1.242:8087/images/reddit.png", alt: "Old Reddit" },
+ { href: "http://forums.crackberry.com/", label: "CB Forums", image: "http://192.168.1.242:8087/images/cb.png", alt: "CB Forums" },
+
+ ]
+ },
+
+ search: {
+ title: "Search",
+ links: [
+ { href: "https://html.duckduckgo.com/html/", label: "DuckDuckGo", image: "https://duckduckgo.com/assets/logo_header.v109.svg", alt: "DuckDuckGo" },
+ { href: "https://frogfind.sumisu.xyz/", label: "FrogFind! Search", image: "http://frogfind.de/frosch.gif", alt: "FrogFind! Search" },
+ { href: "http://wiby.me/", label: "Wiby", image: "http://192.168.1.242:8087/images/wiby.png", alt: "Wiby" },
+ ]
+ },
+
+ games: {
+ title: "Games",
+ links: [
+ { href: "./wordle/", label: "Wordle", image: "https://upload.wikimedia.org/wikipedia/commons/c/c5/Wordle_Logo.svg", alt: "Wordle" },
+ ]
+ }
+ };
+
\ No newline at end of file