From ef84bb2c6381fe70b43c72a06a6f73d6777cc6d6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=AAsufian=20nasser=E2=80=AC=E2=80=8F?=
<166199800+sufyanaser@users.noreply.github.com>
Date: Mon, 20 Jul 2026 18:29:34 +0300
Subject: [PATCH 1/4] Add projects library and separate project editor view
---
index.html | 38 ++++++++++++++------------------------
1 file changed, 14 insertions(+), 24 deletions(-)
diff --git a/index.html b/index.html
index fc19893..89b73e7 100644
--- a/index.html
+++ b/index.html
@@ -2,37 +2,27 @@
NASMIX
-
+
-
+
+
+
+
+
+لا توجد مشاريع محفوظةأنشئ مشروع أغنية، ثم ستظهر بطاقته هنا تلقائيًا.
+
+
01 Song Brief02 Structure03 Track Plan04 Suno Reference05 Generate06 Review07 Cubase08 Mix09 Export
التقدم0 / 9
أدخل بيانات الأغنية لفتح المشروع
تهيئة المشروع…
-
-
-
-
+
-
-CategoryCustom
Weirdness10%
Style Influence100%
Audio Influence0%
-
-
+
CategoryCustom
Weirdness10%
Style Influence100%
Audio Influence0%
+
+
\ No newline at end of file
From 350e1993bae720d93d38f52f42ca66ac45865df5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=AAsufian=20nasser=E2=80=AC=E2=80=8F?=
<166199800+sufyanaser@users.noreply.github.com>
Date: Mon, 20 Jul 2026 18:30:11 +0300
Subject: [PATCH 2/4] Add interactive saved projects library
---
ui/projects-library.js | 122 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 122 insertions(+)
create mode 100644 ui/projects-library.js
diff --git a/ui/projects-library.js b/ui/projects-library.js
new file mode 100644
index 0000000..8e73377
--- /dev/null
+++ b/ui/projects-library.js
@@ -0,0 +1,122 @@
+(() => {
+ "use strict";
+
+ const $ = (id) => document.getElementById(id);
+ let projects = [];
+
+ const formatDate = (value) => {
+ if (!value) return "—";
+ try { return new Intl.DateTimeFormat("ar-IQ", { dateStyle:"medium", timeStyle:"short" }).format(new Date(value)); }
+ catch { return "—"; }
+ };
+
+ const completedCount = (project) => new Set(project.completedStages || []).size;
+ const safe = (value) => String(value || "").replace(/[&<>'"]/g, (c) => ({"&":"&","<":"<",">":">","'":"'",'"':"""}[c]));
+
+ function showLibrary() {
+ history.replaceState(null, "", location.pathname + location.search);
+ $("projectsLibrary").hidden = false;
+ $("projectEditorView").hidden = true;
+ $("backToProjectsButton").hidden = true;
+ $("appPageTitle").textContent = "مشاريع التوزيع الموسيقي";
+ render().catch(console.error);
+ }
+
+ function openProject(id) {
+ window.NASMixProjectStore.setActive(id);
+ location.hash = `project=${encodeURIComponent(id)}`;
+ location.reload();
+ }
+
+ async function createProject() {
+ const project = await window.NASMixProjectStore.save(window.NASMixProjectStore.createProject("مشروع أغنية جديد"));
+ openProject(project.id);
+ }
+
+ async function importProject(file) {
+ const parsed = JSON.parse(await file.text());
+ if (!parsed?.brief) throw new Error("Invalid NASMIX project");
+ parsed.id = window.NASMixProjectStore.createId("project");
+ parsed.name = parsed.name || parsed.brief.songName || "مشروع مستورد";
+ const project = await window.NASMixProjectStore.save(parsed);
+ openProject(project.id);
+ }
+
+ function card(project) {
+ const brief = project.brief || {};
+ const count = completedCount(project);
+ const title = project.name || brief.songName || "مشروع بدون اسم";
+ const meta = [brief.tempo ? `${brief.tempo} BPM` : null, brief.key, brief.maqam, brief.timeSignature].filter(Boolean).join(" · ") || "لم تُدخل بيانات الأغنية بعد";
+ return `
+ المرحلة ${String(project.currentStage || 1).padStart(2,"0")}${formatDate(project.updatedAt)}
+ ${safe(title)}
${safe(meta)}
+ ${safe(brief.mainInstrument || "بدون آلة قائدة")}${safe(brief.primaryGroove || "بدون إيقاع")}
+
+ `;
+ }
+
+ function applyFilter() {
+ const query = String($("projectSearchInput").value || "").trim().toLowerCase();
+ const filtered = query ? projects.filter((project) => {
+ const brief = project.brief || {};
+ return [project.name, brief.songName, brief.maqam, brief.mainInstrument, brief.primaryGroove, brief.mood].some((value) => String(value || "").toLowerCase().includes(query));
+ }) : projects;
+ $("projectsGrid").innerHTML = filtered.map(card).join("");
+ $("emptyProjectsState").hidden = projects.length !== 0;
+ $("projectsGrid").hidden = projects.length === 0;
+ $("projectsCount").textContent = `${projects.length} مشروع`;
+ }
+
+ async function render() {
+ projects = await window.NASMixProjectStore.list();
+ applyFilter();
+ }
+
+ function bind() {
+ $("libraryNewProjectButton").addEventListener("click", createProject);
+ $("emptyNewProjectButton").addEventListener("click", createProject);
+ $("backToProjectsButton").addEventListener("click", showLibrary);
+ $("projectSearchInput").addEventListener("input", applyFilter);
+ $("projectsGrid").addEventListener("click", (event) => {
+ const target = event.target.closest("[data-open-project], .project-card");
+ const id = target?.dataset.openProject || target?.dataset.projectId;
+ if (id) openProject(id);
+ });
+ $("projectsGrid").addEventListener("keydown", (event) => {
+ if ((event.key === "Enter" || event.key === " ") && event.target.classList.contains("project-card")) {
+ event.preventDefault(); openProject(event.target.dataset.projectId);
+ }
+ });
+ $("libraryImportInput").addEventListener("change", async (event) => {
+ const file = event.target.files?.[0];
+ if (!file) return;
+ try { await importProject(file); }
+ catch { alert("ملف المشروع غير صالح"); }
+ event.target.value = "";
+ });
+ }
+
+ async function init() {
+ if (!window.NASMixProjectStore || !$("projectsLibrary")) return;
+ bind();
+ const match = location.hash.match(/^#project=(.+)$/);
+ if (match) {
+ const id = decodeURIComponent(match[1]);
+ const project = await window.NASMixProjectStore.get(id);
+ if (project) {
+ window.NASMixProjectStore.setActive(id);
+ $("projectsLibrary").hidden = true;
+ $("projectEditorView").hidden = false;
+ $("backToProjectsButton").hidden = false;
+ $("appPageTitle").textContent = project.name || project.brief?.songName || "مشروع الأغنية";
+ return;
+ }
+ }
+ showLibrary();
+ }
+
+ window.addEventListener("DOMContentLoaded", () => init().catch(console.error));
+ window.addEventListener("nasmix:project-loaded", (event) => {
+ if (!$("projectEditorView").hidden) $("appPageTitle").textContent = event.detail?.name || event.detail?.brief?.songName || "مشروع الأغنية";
+ });
+})();
\ No newline at end of file
From 7aacfcd024cc55efd509e41fa3b3a8047aa0e557 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=AAsufian=20nasser=E2=80=AC=E2=80=8F?=
<166199800+sufyanaser@users.noreply.github.com>
Date: Mon, 20 Jul 2026 18:30:31 +0300
Subject: [PATCH 3/4] Style projects library dashboard
---
ui/projects-library.css | 1 +
1 file changed, 1 insertion(+)
create mode 100644 ui/projects-library.css
diff --git a/ui/projects-library.css b/ui/projects-library.css
new file mode 100644
index 0000000..7ffe48e
--- /dev/null
+++ b/ui/projects-library.css
@@ -0,0 +1 @@
+.projects-library{padding:22px}.library-heading{display:flex;align-items:flex-end;justify-content:space-between;gap:18px;padding:20px;border:1px solid var(--line);border-radius:18px;background:var(--panel)}.library-heading h2{font-size:26px;margin-top:5px}.library-heading p{margin:8px 0 0;color:var(--muted);max-width:720px}.library-kicker{color:var(--accent);font-size:12px;font-weight:800}.library-actions{display:flex;gap:9px;flex-wrap:wrap}.library-tools{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;margin:16px 0}.library-search{width:min(420px,100%)}.projects-count{color:var(--muted);font-size:13px}.projects-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}.project-card{display:grid;gap:16px;min-height:260px;padding:16px;border:1px solid var(--line);border-radius:18px;background:var(--panel);cursor:pointer;transition:transform .16s ease,border-color .16s ease,box-shadow .16s ease}.project-card:hover,.project-card:focus{transform:translateY(-2px);border-color:var(--accent);box-shadow:0 14px 32px var(--shadow);outline:none}.project-card-top,.project-card-footer{display:flex;align-items:center;justify-content:space-between;gap:10px}.project-stage{padding:6px 9px;border-radius:999px;background:var(--accent-soft);color:var(--accent);font-size:11px}.project-updated{font-size:11px;color:var(--muted)}.project-card-main h3{font-size:20px}.project-card-main p{margin:7px 0 0;color:var(--muted);direction:ltr;text-align:right}.project-tags{display:flex;gap:7px;flex-wrap:wrap}.project-tags span{padding:6px 8px;border:1px solid var(--line);border-radius:999px;color:var(--label);font-size:11px;background:var(--input)}.project-card-footer{margin-top:auto}.project-card-footer>div:first-child{display:grid}.project-card-footer strong{font-size:17px}.project-card-footer span{font-size:10px;color:var(--muted)}.project-progress-track{flex:1;height:6px;border-radius:999px;background:var(--input);overflow:hidden}.project-progress-track i{display:block;height:100%;background:linear-gradient(90deg,var(--accent),var(--accent-2));border-radius:inherit}.open-project-button{min-height:34px;padding:7px 10px;font-size:11px}.empty-projects{padding:60px 20px;text-align:center;border:1px dashed var(--line-strong);border-radius:18px;background:var(--panel)}.empty-projects strong{font-size:20px}.empty-projects p{color:var(--muted)}#backToProjectsButton{min-height:38px}@media(max-width:980px){.projects-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.library-heading{align-items:stretch;flex-direction:column}}@media(max-width:620px){.projects-library{padding:12px}.projects-grid{grid-template-columns:1fr}.library-tools{align-items:stretch;flex-direction:column}.library-actions{display:grid;grid-template-columns:1fr}.project-card{min-height:230px}}
\ No newline at end of file
From b4e248482577fb0b6d448b09ff58c3cbc0a8475c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=AAsufian=20nasser=E2=80=AC=E2=80=8F?=
<166199800+sufyanaser@users.noreply.github.com>
Date: Mon, 20 Jul 2026 18:30:51 +0300
Subject: [PATCH 4/4] Cache projects library assets
---
sw.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sw.js b/sw.js
index cb23d56..b8e8736 100644
--- a/sw.js
+++ b/sw.js
@@ -1,7 +1,7 @@
-const CACHE_NAME = "nasmix-shell-v7";
+const CACHE_NAME = "nasmix-shell-v8";
const APP_SHELL = [
- "./","./index.html","./ui/styles.css","./ui/prompt-safety.css","./ui/compact-brief.css","./ui/app.js",
- "./ui/project-store.js","./ui/song-brief.js","./ui/project-workspace.js","./ui/project-bootstrap.js",
+ "./","./index.html","./ui/styles.css","./ui/prompt-safety.css","./ui/compact-brief.css","./ui/projects-library.css","./ui/app.js",
+ "./ui/project-store.js","./ui/song-brief.js","./ui/project-workspace.js","./ui/project-bootstrap.js","./ui/projects-library.js",
"./data/catalog.json","./data/presets-acoustic.json","./data/presets-modern.json","./data/project-schema.json",
"./data/knowledge/manifest.json","./data/knowledge/reliability.json","./data/knowledge/evidence-registry.json",
"./data/knowledge/workflow-stages.json","./data/knowledge/musical-roles.json","./manifest.webmanifest","./assets/icon.svg"