-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy patheditor.js
More file actions
121 lines (110 loc) · 5.29 KB
/
Copy patheditor.js
File metadata and controls
121 lines (110 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/* Editor logic: GitHub OAuth (web flow, token exchanged by /api/token),
load + live-preview + commit content/shepherd.md to the gh-pages repo.
Committing triggers the repo's Action, which rebuilds and deploys the blog. */
(function () {
// Set after you create the GitHub OAuth App (Client ID is public; the secret
// lives only in the /api/token Pages Function).
const CLIENT_ID = "Ov23liHJaf1lXTaa19Ze";
const REPO = "shepherd-agents/shepherd-gh-pages";
const PATH = "content/shepherd.md";
const BRANCH = "main";
const SCOPE = "public_repo"; // repo is public, so this is enough
const $ = (id) => document.getElementById(id);
let token = localStorage.getItem("gh_token") || null;
let sha = null, timer = null;
const status = (s) => { $("ed-status").textContent = s; };
const preview = () => {
const pane = $("ed-preview");
const src = $("ed-src").value;
if (!src.trim()) { pane.innerHTML = '<p style="color:#888">Nothing loaded yet. Sign in, then Reload to pull the post.</p>'; return; }
if (typeof renderDialect !== "function") { pane.innerHTML = '<p style="color:#c0392b">Preview engine did not load (marked.js may be blocked). Check the browser console.</p>'; return; }
try { pane.innerHTML = renderDialect(src); }
catch (e) { pane.innerHTML = '<pre style="color:#c0392b;white-space:pre-wrap">Preview error: ' + e.message + '</pre>'; console.error(e); }
};
function signedIn() {
const b = $("ed-signin");
b.textContent = "Sign out";
b.onclick = () => { localStorage.removeItem("gh_token"); token = null; location.reload(); };
$("ed-load").disabled = false;
$("ed-commit").disabled = false;
status("signed in");
}
async function gh(path, opts = {}) {
return fetch("https://api.github.com/" + path, {
...opts,
headers: { Authorization: "token " + token, Accept: "application/vnd.github+json", ...(opts.headers || {}) },
});
}
function b64decodeUtf8(b64) { return decodeURIComponent(escape(atob(b64.replace(/\n/g, "")))); }
function b64encodeUtf8(str) { return btoa(unescape(encodeURIComponent(str))); }
async function load() {
status("loading…");
const r = await gh(`repos/${REPO}/contents/${PATH}?ref=${BRANCH}`);
if (!r.ok) { status("load failed (" + r.status + ")"); return; }
const d = await r.json();
sha = d.sha;
$("ed-src").value = b64decodeUtf8(d.content);
preview();
status("loaded");
}
async function commit() {
if (!token) { status("sign in first"); return; }
const msg = prompt("Commit message:", "edit blog via editor");
if (!msg) return;
status("committing…");
const body = JSON.stringify({ message: msg, content: b64encodeUtf8($("ed-src").value), sha, branch: BRANCH });
const r = await gh(`repos/${REPO}/contents/${PATH}`, { method: "PUT", body });
if (r.ok) { sha = (await r.json()).content.sha; status("committed ✓ — deploying (~1 min)"); return; }
let m = ""; try { m = (await r.json()).message || ""; } catch {}
let scopes = "";
try { const u = await gh("user"); scopes = u.headers.get("X-OAuth-Scopes") || "(none)"; } catch {}
status(`commit failed (${r.status}): ${m} [token scopes: ${scopes}]`);
console.error("commit failed", r.status, m, "scopes:", scopes);
}
// --- OAuth web flow ---
$("ed-signin").onclick = () => {
const redirect = location.origin + location.pathname;
location.href = "https://github.com/login/oauth/authorize"
+ `?client_id=${CLIENT_ID}&scope=${SCOPE}&redirect_uri=${encodeURIComponent(redirect)}`;
};
const code = new URLSearchParams(location.search).get("code");
if (code) {
status("authorizing…");
fetch("/api/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ code }) })
.then((r) => r.json())
.then((d) => {
history.replaceState({}, "", location.pathname);
if (d.access_token) { token = d.access_token; localStorage.setItem("gh_token", token); signedIn(); load(); }
else status("auth failed: " + (d.error || "unknown"));
})
.catch(() => status("auth request failed"));
} else if (token) {
signedIn();
load();
}
$("ed-load").onclick = load;
$("ed-commit").onclick = commit;
$("ed-src").addEventListener("input", () => { clearTimeout(timer); timer = setTimeout(preview, 200); });
// Draggable divider between editor and preview (persisted).
(function () {
const split = $("ed-split"), gutter = $("ed-gutter");
if (!split || !gutter) return;
const saved = localStorage.getItem("ed_leftw");
if (saved) split.style.setProperty("--leftw", saved);
let drag = false;
gutter.addEventListener("mousedown", (e) => {
drag = true; gutter.classList.add("dragging"); document.body.style.cursor = "col-resize"; e.preventDefault();
});
window.addEventListener("mousemove", (e) => {
if (!drag) return;
const r = split.getBoundingClientRect();
const pct = Math.max(15, Math.min(85, ((e.clientX - r.left) / r.width) * 100));
split.style.setProperty("--leftw", pct + "%");
});
window.addEventListener("mouseup", () => {
if (!drag) return;
drag = false; gutter.classList.remove("dragging"); document.body.style.cursor = "";
localStorage.setItem("ed_leftw", split.style.getPropertyValue("--leftw"));
});
})();
})();