jekyll-theme-zer0 1.28.0 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1143 -13
- data/_data/README.md +2 -0
- data/_data/ai.yml +5 -3
- data/_data/ai_pricing.yml +36 -0
- data/_data/backlog.yml +507 -2
- data/_data/consumers.yml +157 -9
- data/_data/features.yml +303 -20
- data/_data/feedback_types.yml +17 -12
- data/_data/i18n/fr.yml +12 -7
- data/_data/i18n/manifest.yml +39 -12
- data/_data/ingredient_densities.yml +122 -0
- data/_data/landing.yml +5 -2
- data/_data/navigation/main.yml +16 -0
- data/_data/navigation/quickstart.yml +4 -0
- data/_data/recipe_courses.yml +64 -0
- data/_data/site_builder.yml +874 -0
- data/_data/theme-manifest.yml +160 -124
- data/_data/ui-text.yml +26 -0
- data/_includes/README.md +26 -2
- data/_includes/analytics/posthog.html +2 -2
- data/_includes/components/admin-links.html +2 -2
- data/_includes/components/admin-tabs.html +2 -2
- data/_includes/components/ai-chat.html +14 -11
- data/_includes/components/analytics-dashboard.html +8 -8
- data/_includes/components/author-bio.html +1 -1
- data/_includes/components/author-card.html +10 -2
- data/_includes/components/author-eeat.html +4 -4
- data/_includes/components/background-customizer.html +10 -10
- data/_includes/components/background-image.html +114 -0
- data/_includes/components/background-settings.html +28 -15
- data/_includes/components/collection-manager.html +5 -5
- data/_includes/components/component-showcase.html +13 -13
- data/_includes/components/config-editor.html +12 -12
- data/_includes/components/config-viewer.html +8 -8
- data/_includes/components/cookie-consent.html +15 -15
- data/_includes/components/cta-button.html +7 -2
- data/_includes/components/dev-shortcuts.html +7 -7
- data/_includes/components/env-dashboard.html +8 -8
- data/_includes/components/env-switcher.html +9 -9
- data/_includes/components/feature-card.html +2 -2
- data/_includes/components/halfmoon.html +2 -2
- data/_includes/components/info-section.html +42 -37
- data/_includes/components/js-cdn.html +15 -15
- data/_includes/components/language-toggle.html +168 -21
- data/_includes/components/mermaid.html +72 -435
- data/_includes/components/nanobar.html +5 -5
- data/_includes/components/nav-editor.html +2 -2
- data/_includes/components/nav-export.html +2 -2
- data/_includes/components/nav-overview.html +2 -2
- data/_includes/components/page-feedback.html +45 -30
- data/_includes/components/page-views-init.html +55 -0
- data/_includes/components/page-views.html +33 -0
- data/_includes/components/post-card.html +22 -22
- data/_includes/components/post-type-badge.html +2 -2
- data/_includes/components/powered-by.html +2 -2
- data/_includes/components/preview-image.html +6 -0
- data/_includes/components/quick-index.html +2 -2
- data/_includes/components/recipe-card.html +67 -0
- data/_includes/components/recipe-duration.html +50 -0
- data/_includes/components/recipe-grams.html +58 -0
- data/_includes/components/recipe-index.html +96 -0
- data/_includes/components/recipe-ingredients.html +90 -0
- data/_includes/components/recipe-meta.html +96 -0
- data/_includes/components/recipe-nutrition.html +57 -0
- data/_includes/components/recipe-qty.html +73 -0
- data/_includes/components/recipe-ratio.html +151 -0
- data/_includes/components/recipe-scaler.html +73 -0
- data/_includes/components/recipe-steps.html +86 -0
- data/_includes/components/recipe-temp.html +45 -0
- data/_includes/components/search-modal.html +29 -4
- data/_includes/components/searchbar.html +2 -2
- data/_includes/components/shortcuts-modal.html +3 -0
- data/_includes/components/svg-background.html +2 -2
- data/_includes/components/theme-customizer.html +2 -2
- data/_includes/components/theme-info.html +14 -7
- data/_includes/components/theme-preview-gallery.html +22 -22
- data/_includes/content/giscus.html +2 -2
- data/_includes/content/intro.html +8 -8
- data/_includes/content/jsonld-faq.html +2 -2
- data/_includes/content/jsonld-software.html +24 -5
- data/_includes/content/seo.html +4 -4
- data/_includes/content/sitemap.html +27 -27
- data/_includes/content/toc.html +183 -183
- data/_includes/core/branding.html +6 -6
- data/_includes/core/console-capture.html +32 -74
- data/_includes/core/favicon.html +49 -7
- data/_includes/core/footer-fabs.html +17 -3
- data/_includes/core/footer.html +49 -34
- data/_includes/core/head.html +110 -86
- data/_includes/core/header.html +76 -54
- data/_includes/docs/bootstrap-docs.html +8 -8
- data/_includes/landing/landing-install-cards.html +2 -2
- data/_includes/landing/landing-quick-links.html +1 -1
- data/_includes/navigation/admin-nav.html +2 -2
- data/_includes/navigation/nav-tree.html +8 -8
- data/_includes/navigation/navbar.html +12 -12
- data/_includes/navigation/section-sidebar.html +109 -27
- data/_includes/navigation/sidebar-config.html +36 -2
- data/_includes/navigation/sidebar-left.html +17 -16
- data/_includes/navigation/sidebar-right.html +8 -7
- data/_includes/obsidian/full-graph.html +2 -2
- data/_includes/setup/claude-session.html +72 -0
- data/_includes/setup/prereq-checklist.html +90 -0
- data/_includes/setup/wizard.html +924 -222
- data/_includes/stats/stats-categories.html +8 -8
- data/_includes/stats/stats-header.html +14 -14
- data/_includes/stats/stats-metrics.html +14 -14
- data/_includes/stats/stats-no-data.html +12 -12
- data/_includes/stats/stats-overview.html +6 -6
- data/_includes/stats/stats-tags.html +8 -8
- data/_layouts/404.html +38 -24
- data/_layouts/README.md +2 -0
- data/_layouts/admin.html +24 -24
- data/_layouts/article.html +43 -33
- data/_layouts/author.html +20 -20
- data/_layouts/authors.html +2 -2
- data/_layouts/book-abc.html +12 -12
- data/_layouts/book-story.html +15 -15
- data/_layouts/book.html +12 -12
- data/_layouts/collection.html +33 -33
- data/_layouts/cookbook.html +88 -0
- data/_layouts/default.html +31 -28
- data/_layouts/home.html +23 -23
- data/_layouts/index.html +10 -10
- data/_layouts/landing.html +17 -17
- data/_layouts/news.html +44 -44
- data/_layouts/note.html +38 -38
- data/_layouts/notebook.html +34 -34
- data/_layouts/recipe.html +274 -0
- data/_layouts/root.html +92 -55
- data/_layouts/section.html +62 -33
- data/_layouts/setup.html +3 -3
- data/_layouts/sitemap-collection.html +49 -49
- data/_layouts/stats.html +40 -40
- data/_layouts/tag.html +12 -12
- data/_layouts/welcome.html +21 -21
- data/_sass/components/_callout.scss +1 -1
- data/_sass/components/_footer.scss +37 -1
- data/_sass/components/_mermaid.scss +375 -0
- data/_sass/components/_page-views.scss +36 -0
- data/_sass/components/_recipe.scss +506 -0
- data/_sass/components/_setup-wizard.scss +764 -0
- data/_sass/components/_ui-enhancements.scss +6 -6
- data/_sass/core/_navbar.scss +261 -46
- data/_sass/layouts/_landing.scss +2 -2
- data/_sass/layouts/_navbar-extras.scss +14 -4
- data/_sass/tokens/_color.scss +6 -0
- data/_sass/tokens/_index.scss +2 -0
- data/_sass/tokens/_radius.scss +21 -0
- data/_sass/tokens/_typography.scss +4 -0
- data/_sass/utilities/_focus.scss +14 -0
- data/assets/css/main.scss +4 -0
- data/assets/js/ai-chat.js +47 -5
- data/assets/js/fleet-feedback-capture.js +124 -0
- data/assets/js/fleet-feedback.js +853 -0
- data/assets/js/mermaid-diagrams.js +1267 -0
- data/assets/js/modules/navigation/config.js +9 -6
- data/assets/js/modules/navigation/navbar.js +55 -0
- data/assets/js/modules/navigation/scroll-spy.js +315 -80
- data/assets/js/modules/theme/appearance.js +8 -2
- data/assets/js/obsidian-wiki-links.js +8 -3
- data/assets/js/page-feedback.js +125 -192
- data/assets/js/page-views.js +372 -0
- data/assets/js/recipe-scaler.js +501 -0
- data/assets/js/search-modal.js +36 -0
- data/assets/js/setup-wizard.js +2279 -226
- data/assets/js/site-builder.js +1834 -0
- data/assets/js/ui-enhancements.js +11 -3
- data/scripts/README.md +44 -0
- data/scripts/ai/README.md +38 -0
- data/scripts/ai/api_call.rb +124 -0
- data/scripts/ai/usage.rb +314 -0
- data/scripts/ai/usage_report.rb +225 -0
- data/scripts/bin/audit-consumer +39 -7
- data/scripts/bin/giscus-discussions +213 -14
- data/scripts/bin/manifest +35 -12
- data/scripts/ci/agent_review_result.py +164 -0
- data/scripts/ci/test_agent_review_result.py +172 -0
- data/scripts/ci/test_visual_evidence_autogen.py +341 -0
- data/scripts/ci/visual_evidence_autogen.py +1060 -0
- data/scripts/content-review.rb +20 -1
- data/scripts/design-system-check.rb +170 -0
- data/scripts/lib/audit.sh +42 -2
- data/scripts/lint-liquid-raw.rb +137 -0
- data/scripts/test/integration/mermaid +22 -8
- data/scripts/test/lib/run_tests.sh +3 -1
- data/scripts/test/lib/test_agent_review_result.sh +27 -0
- data/scripts/test/lib/test_visual_evidence_autogen.sh +24 -0
- data/scripts/translate.rb +94 -16
- metadata +48 -2
data/assets/js/page-feedback.js
CHANGED
|
@@ -5,36 +5,52 @@
|
|
|
5
5
|
*
|
|
6
6
|
* File: assets/js/page-feedback.js
|
|
7
7
|
* Path: assets/js/page-feedback.js
|
|
8
|
-
* Purpose:
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* no token) or
|
|
12
|
-
* an optional AI clarify/vet/prioritize
|
|
8
|
+
* Purpose: The theme's UI for turning what a reader is looking at into a
|
|
9
|
+
* well-formed GitHub issue — request type, description, live page
|
|
10
|
+
* context, captured console logs — opened as a pre-filled
|
|
11
|
+
* github.com form (base layer, no token) or filed through the chat
|
|
12
|
+
* proxy (proxy mode), with an optional AI clarify/vet/prioritize
|
|
13
|
+
* step in between.
|
|
14
|
+
*
|
|
15
|
+
* THE ISSUE BODY IS NOT BUILT HERE. It comes from FleetFeedbackCore in the
|
|
16
|
+
* vendored assets/js/fleet-feedback.js (bamr87/bamr87 templates/feedback,
|
|
17
|
+
* spec specs/FEEDBACK.md UPS-FB) — the same builder the fleet's
|
|
18
|
+
* <fleet-feedback> web component uses. This file owns the Bootstrap modal,
|
|
19
|
+
* the AI enrichment step, and the submit paths; the core owns the contract:
|
|
20
|
+
* section order, the tables, the `<!-- fleet-feedback v1 type=... -->` marker
|
|
21
|
+
* the issue pipeline reads, labels, assignment, and the 7000-char URL budget
|
|
22
|
+
* with its trim ladder and clipboard fallback.
|
|
23
|
+
*
|
|
24
|
+
* That split is the point. This widget and the fleet component are two UIs
|
|
25
|
+
* over one contract, so an issue filed from a Jekyll page and one filed from
|
|
26
|
+
* a Next.js dashboard are byte-identical to the pipeline that triages them —
|
|
27
|
+
* instead of two shapes that drift apart a release at a time.
|
|
13
28
|
*
|
|
14
29
|
* Reads three JSON blocks injected by _includes/components/page-feedback.html:
|
|
15
30
|
* #pageFeedbackConfig — mode, endpoints, repo, labels, assignee, ai flags
|
|
16
31
|
* #pageFeedbackTypes — the request-type taxonomy (_data/feedback_types.yml)
|
|
17
32
|
* #pageFeedbackContext — build-time page/front-matter context
|
|
18
33
|
*
|
|
19
|
-
* Console/error logs come from
|
|
20
|
-
* (window.
|
|
34
|
+
* Console/error logs come from the same kit's capture buffer
|
|
35
|
+
* (window.__fleetFeedback), installed in <head> by core/console-capture.html
|
|
36
|
+
* and redacted on the way in.
|
|
21
37
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* - GitHub silently drops labels that don't exist, so the taxonomy maps to
|
|
26
|
-
* labels that do (see _data/feedback_types.yml).
|
|
27
|
-
* - Pre-filled issue URLs have a practical length ceiling; buildPrefillUrl()
|
|
28
|
-
* trims low-priority sections and falls back to the clipboard so nothing
|
|
29
|
-
* is silently lost.
|
|
38
|
+
* Note: GitHub silently drops labels that don't exist — no error, no warning
|
|
39
|
+
* — so the taxonomy maps onto the fleet issue-pipeline label set, all of
|
|
40
|
+
* which exist in this repo (see _data/feedback_types.yml).
|
|
30
41
|
* ===================================================================
|
|
31
42
|
*/
|
|
32
43
|
(function () {
|
|
33
44
|
"use strict";
|
|
34
45
|
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
46
|
+
// The shared issue builder (assets/js/fleet-feedback.js, vendored from the
|
|
47
|
+
// hub kit). Without it there is no contract to emit, so bail rather than
|
|
48
|
+
// silently falling back to a second, divergent body format.
|
|
49
|
+
var FF = window.FleetFeedbackCore;
|
|
50
|
+
if (!FF) {
|
|
51
|
+
console.warn("page-feedback: FleetFeedbackCore is missing — is assets/js/fleet-feedback.js loaded?");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
38
54
|
|
|
39
55
|
function readJSON(id, fallback) {
|
|
40
56
|
var el = document.getElementById(id);
|
|
@@ -91,41 +107,44 @@
|
|
|
91
107
|
return null;
|
|
92
108
|
}
|
|
93
109
|
|
|
94
|
-
// ---
|
|
95
|
-
|
|
96
|
-
|
|
110
|
+
// --- page + environment, as the shared builder expects them ----------
|
|
111
|
+
// The core owns which rows the tables carry; the extra `rows` are this
|
|
112
|
+
// stack's own detail (Jekyll build env, theme, layout) appended after the
|
|
113
|
+
// contract rows rather than replacing them.
|
|
114
|
+
function pageContext() {
|
|
97
115
|
return {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
reducedMotion: !!(mm && mm("(prefers-reduced-motion: reduce)").matches),
|
|
106
|
-
time: new Date().toISOString()
|
|
116
|
+
title: CONTEXT.title || document.title,
|
|
117
|
+
url: location.href,
|
|
118
|
+
source: CONTEXT.path || "",
|
|
119
|
+
sourceUrl: CONTEXT.sourceUrl || "",
|
|
120
|
+
route: CONTEXT.collection || CONTEXT.url || "",
|
|
121
|
+
lastmod: CONTEXT.lastmod || "",
|
|
122
|
+
rows: CONTEXT.layout ? [["Layout", CONTEXT.layout]] : []
|
|
107
123
|
};
|
|
108
124
|
}
|
|
109
125
|
|
|
126
|
+
function environment() {
|
|
127
|
+
var rows = [];
|
|
128
|
+
if (CONTEXT.theme) rows.push(["Theme", CONTEXT.theme]);
|
|
129
|
+
return FF.collectEnvironment({
|
|
130
|
+
repository: CONTEXT.repository || CONFIG.repository,
|
|
131
|
+
branch: CONTEXT.branch || CONFIG.baseBranch,
|
|
132
|
+
buildEnv: CONTEXT.jekyllEnv || "",
|
|
133
|
+
rows: rows
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
110
137
|
function snapshotLogs() {
|
|
111
|
-
var store = window.
|
|
138
|
+
var store = window.__fleetFeedback;
|
|
112
139
|
if (!store || typeof store.snapshot !== "function") return [];
|
|
113
140
|
try { return store.snapshot(); } catch (err) { return []; }
|
|
114
141
|
}
|
|
115
142
|
|
|
116
|
-
function formatLogs(logs) {
|
|
117
|
-
return logs.map(function (entry) {
|
|
118
|
-
var level = String(entry.level || "log").toUpperCase();
|
|
119
|
-
return "[" + entry.t + "ms] " + level + " " + entry.text;
|
|
120
|
-
}).join("\n");
|
|
121
|
-
}
|
|
122
|
-
|
|
123
143
|
// --- rendering --------------------------------------------------------
|
|
124
144
|
function renderContext() {
|
|
125
|
-
var rt = runtimeContext();
|
|
126
145
|
var rows = [
|
|
127
146
|
["Page", CONTEXT.title || document.title],
|
|
128
|
-
["URL",
|
|
147
|
+
["URL", location.href]
|
|
129
148
|
];
|
|
130
149
|
if (CONTEXT.sourceUrl) {
|
|
131
150
|
rows.push(["Source", CONTEXT.path || CONTEXT.sourceUrl]);
|
|
@@ -151,7 +170,7 @@
|
|
|
151
170
|
if (count > 0) {
|
|
152
171
|
els.logsWrap.hidden = false;
|
|
153
172
|
if (els.logsCount) els.logsCount.textContent = String(count);
|
|
154
|
-
if (els.logsPreview) els.logsPreview.textContent = formatLogs(state.logs);
|
|
173
|
+
if (els.logsPreview) els.logsPreview.textContent = FF.formatLogs(state.logs);
|
|
155
174
|
} else {
|
|
156
175
|
els.logsWrap.hidden = true;
|
|
157
176
|
}
|
|
@@ -195,156 +214,50 @@
|
|
|
195
214
|
return ok;
|
|
196
215
|
}
|
|
197
216
|
|
|
198
|
-
// --- issue assembly
|
|
199
|
-
|
|
200
|
-
//
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
return out;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function issueLabels(type) {
|
|
218
|
-
var labels = (CONFIG.defaultLabels || []).concat(type && type.labels ? type.labels : []);
|
|
219
|
-
if (state.analysis && state.analysis.labels) labels = labels.concat(state.analysis.labels);
|
|
220
|
-
if (state.analysis && state.analysis.priority) labels.push("priority:" + state.analysis.priority);
|
|
221
|
-
return dedupe(labels);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Build the issue body in named sections so the URL guard can drop the
|
|
225
|
-
// low-priority ones (logs, then agent directive) without losing the core.
|
|
226
|
-
function buildSections(type, opts) {
|
|
227
|
-
var rt = runtimeContext();
|
|
228
|
-
var includeLogs = opts.includeLogs && state.logs.length > 0;
|
|
229
|
-
var sections = {};
|
|
230
|
-
|
|
231
|
-
sections.description = els.description.value.trim();
|
|
232
|
-
|
|
233
|
-
if (state.analysis) {
|
|
234
|
-
var a = state.analysis;
|
|
235
|
-
var ai = ["## 🤖 AI review", ""];
|
|
236
|
-
if (a.summary) ai.push(a.summary, "");
|
|
237
|
-
var meta = [];
|
|
238
|
-
if (a.priority) meta.push("**Priority:** " + a.priority);
|
|
239
|
-
if (a.severity) meta.push("**Severity:** " + a.severity);
|
|
240
|
-
if (meta.length) ai.push(meta.join(" · "), "");
|
|
241
|
-
if (a.recommendation) ai.push("**Recommended approach:** " + a.recommendation, "");
|
|
242
|
-
sections.ai = ai.join("\n");
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
sections.context = [
|
|
246
|
-
"## 📄 Page context", "",
|
|
247
|
-
"| Field | Value |", "|---|---|",
|
|
248
|
-
"| **Page** | " + cell(CONTEXT.title) + " |",
|
|
249
|
-
"| **URL** | " + cell(rt.href) + " |",
|
|
250
|
-
CONTEXT.sourceUrl ? "| **Source** | [`" + cell(CONTEXT.path) + "`](" + CONTEXT.sourceUrl + ") |" : "",
|
|
251
|
-
CONTEXT.layout ? "| **Layout** | `" + cell(CONTEXT.layout) + "` |" : "",
|
|
252
|
-
CONTEXT.collection ? "| **Collection** | `" + cell(CONTEXT.collection) + "` |" : ""
|
|
253
|
-
].filter(Boolean).join("\n");
|
|
254
|
-
|
|
255
|
-
sections.environment = [
|
|
256
|
-
"## 🔧 Environment", "",
|
|
257
|
-
"| Field | Value |", "|---|---|",
|
|
258
|
-
"| **Browser** | " + cell(rt.userAgent) + " |",
|
|
259
|
-
"| **Viewport** | " + cell(rt.viewport) + " @ " + rt.dpr + "x |",
|
|
260
|
-
"| **Color scheme** | " + cell(rt.colorScheme) + (rt.reducedMotion ? " · reduced-motion" : "") + " |",
|
|
261
|
-
rt.referrer ? "| **Referrer** | " + cell(rt.referrer) + " |" : "",
|
|
262
|
-
"| **Repository** | `" + cell(CONTEXT.repository) + "` |",
|
|
263
|
-
"| **Branch** | `" + cell(CONTEXT.branch) + "` |",
|
|
264
|
-
"| **Jekyll env** | `" + cell(CONTEXT.jekyllEnv) + "` |",
|
|
265
|
-
"| **Captured** | " + cell(rt.time) + " |"
|
|
266
|
-
].filter(Boolean).join("\n");
|
|
267
|
-
|
|
268
|
-
if (includeLogs) {
|
|
269
|
-
sections.logs = [
|
|
270
|
-
"<details>",
|
|
271
|
-
"<summary>🧾 Console & error logs (" + state.logs.length + ")</summary>",
|
|
272
|
-
"", "```text", formatLogs(state.logs), "```", "",
|
|
273
|
-
"</details>"
|
|
274
|
-
].join("\n");
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (type && type.agent && type.directive) {
|
|
278
|
-
sections.directive = [
|
|
279
|
-
"<details>",
|
|
280
|
-
"<summary>🤖 AI agent instructions</summary>",
|
|
281
|
-
"", type.directive, "",
|
|
282
|
-
"</details>"
|
|
283
|
-
].join("\n");
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
sections.footer = "---\n_Filed from " + rt.href + " via the page-feedback widget._";
|
|
287
|
-
return sections;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function assemble(sections, order) {
|
|
291
|
-
return order
|
|
292
|
-
.map(function (key) { return sections[key]; })
|
|
293
|
-
.filter(function (part) { return part && part.length; })
|
|
294
|
-
.join("\n\n");
|
|
217
|
+
// --- issue assembly (delegated to the shared core) --------------------
|
|
218
|
+
|
|
219
|
+
// The AI review is theme-only enrichment, not part of the fleet contract,
|
|
220
|
+
// so it rides in `extra` — appended to the description with its own heading
|
|
221
|
+
// — rather than forcing a section slot into the shared section order.
|
|
222
|
+
function aiSection() {
|
|
223
|
+
if (!state.analysis) return "";
|
|
224
|
+
var a = state.analysis;
|
|
225
|
+
var out = ["## 🤖 AI review", ""];
|
|
226
|
+
if (a.summary) out.push(a.summary, "");
|
|
227
|
+
var meta = [];
|
|
228
|
+
if (a.priority) meta.push("**Priority:** " + a.priority);
|
|
229
|
+
if (a.severity) meta.push("**Severity:** " + a.severity);
|
|
230
|
+
if (meta.length) out.push(meta.join(" · "), "");
|
|
231
|
+
if (a.recommendation) out.push("**Recommended approach:** " + a.recommendation);
|
|
232
|
+
return out.join("\n").trim();
|
|
295
233
|
}
|
|
296
234
|
|
|
297
235
|
function buildIssue(opts) {
|
|
298
236
|
opts = opts || {};
|
|
299
237
|
var type = typeById(state.typeId);
|
|
300
238
|
var includeLogs = els.includeLogs ? els.includeLogs.checked : false;
|
|
301
|
-
var
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
: (
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
body: assemble(sections, fullOrder),
|
|
313
|
-
labels: issueLabels(type),
|
|
314
|
-
assignees: assignees,
|
|
315
|
-
sections: sections,
|
|
316
|
-
order: fullOrder
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Assemble a prefill URL under the length budget, trimming logs then the
|
|
321
|
-
// agent directive then the environment table. Reports what was dropped so
|
|
322
|
-
// the caller can offer the full body via the clipboard.
|
|
323
|
-
function buildPrefillUrl(issue) {
|
|
324
|
-
var base = "https://github.com/" + CONFIG.repository + "/issues/new";
|
|
325
|
-
|
|
326
|
-
function compose(order) {
|
|
327
|
-
var params = new URLSearchParams();
|
|
328
|
-
params.set("title", issue.title);
|
|
329
|
-
params.set("body", assemble(issue.sections, order));
|
|
330
|
-
if (issue.labels.length) params.set("labels", issue.labels.join(","));
|
|
331
|
-
if (issue.assignees.length) params.set("assignees", issue.assignees.join(","));
|
|
332
|
-
return base + "?" + params.toString();
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
var trimSteps = [
|
|
336
|
-
{ order: ["description", "ai", "context", "environment", "logs", "directive", "footer"], dropped: [] },
|
|
337
|
-
{ order: ["description", "ai", "context", "environment", "directive", "footer"], dropped: ["logs"] },
|
|
338
|
-
{ order: ["description", "ai", "context", "environment", "footer"], dropped: ["logs", "directive"] },
|
|
339
|
-
{ order: ["description", "ai", "context", "footer"], dropped: ["logs", "directive", "environment"] }
|
|
340
|
-
];
|
|
341
|
-
|
|
342
|
-
for (var i = 0; i < trimSteps.length; i++) {
|
|
343
|
-
var url = compose(trimSteps[i].order);
|
|
344
|
-
if (url.length <= MAX_URL_LENGTH || i === trimSteps.length - 1) {
|
|
345
|
-
return { url: url, dropped: trimSteps[i].dropped, overLimit: url.length > MAX_URL_LENGTH };
|
|
239
|
+
var issue = FF.buildIssue({
|
|
240
|
+
type: type,
|
|
241
|
+
description: els.description.value,
|
|
242
|
+
extra: aiSection(),
|
|
243
|
+
page: pageContext(),
|
|
244
|
+
environment: environment(),
|
|
245
|
+
logs: (opts.includeLogs !== false && includeLogs) ? state.logs : [],
|
|
246
|
+
config: {
|
|
247
|
+
labels: CONFIG.defaultLabels || ["page-feedback"],
|
|
248
|
+
assignee: CONFIG.assignee,
|
|
249
|
+
repo: CONFIG.repository
|
|
346
250
|
}
|
|
251
|
+
});
|
|
252
|
+
// The AI step may sharpen the title and add labels/priority. Those are
|
|
253
|
+
// applied on top of the contract, never in place of it.
|
|
254
|
+
if (state.analysis) {
|
|
255
|
+
if (state.analysis.title) issue.title = state.analysis.title.slice(0, 240);
|
|
256
|
+
var extraLabels = (state.analysis.labels || []).slice();
|
|
257
|
+
if (state.analysis.priority) extraLabels.push("priority:" + state.analysis.priority);
|
|
258
|
+
issue.labels = FF.uniq(issue.labels.concat(extraLabels));
|
|
347
259
|
}
|
|
260
|
+
return issue;
|
|
348
261
|
}
|
|
349
262
|
|
|
350
263
|
// --- clipboard --------------------------------------------------------
|
|
@@ -376,7 +289,7 @@
|
|
|
376
289
|
|
|
377
290
|
// --- submit -----------------------------------------------------------
|
|
378
291
|
function submitViaUrl(issue) {
|
|
379
|
-
var built =
|
|
292
|
+
var built = FF.buildUrl(issue, { repo: CONFIG.repository });
|
|
380
293
|
// Open WITHOUT the "noopener" feature so we get a real window reference to
|
|
381
294
|
// detect pop-up blocking, then sever the opener link ourselves.
|
|
382
295
|
var win = window.open(built.url, "_blank");
|
|
@@ -390,10 +303,10 @@
|
|
|
390
303
|
});
|
|
391
304
|
return;
|
|
392
305
|
}
|
|
393
|
-
if (built.
|
|
306
|
+
if (built.trimmed.length) {
|
|
394
307
|
// Something was too long for the URL — hand the full body to the clipboard.
|
|
395
308
|
copyText(issue.body).then(function () {
|
|
396
|
-
setStatus("Opened GitHub. The " + built.
|
|
309
|
+
setStatus("Opened GitHub. The " + built.trimmed.join(" and ") +
|
|
397
310
|
" section(s) were too long for the URL, so the full report is on your clipboard — paste it in.", "warn");
|
|
398
311
|
})["catch"](function () {
|
|
399
312
|
setStatus("Opened GitHub. Some sections were trimmed to fit the URL.", "warn");
|
|
@@ -464,9 +377,9 @@
|
|
|
464
377
|
sourceUrl: CONTEXT.sourceUrl, layout: CONTEXT.layout,
|
|
465
378
|
collection: CONTEXT.collection, repository: CONTEXT.repository, branch: CONTEXT.branch
|
|
466
379
|
},
|
|
467
|
-
environment:
|
|
468
|
-
logs: includeLogs ? formatLogs(state.logs) : "",
|
|
469
|
-
availableLabels:
|
|
380
|
+
environment: environment(),
|
|
381
|
+
logs: includeLogs ? FF.formatLogs(state.logs) : "",
|
|
382
|
+
availableLabels: FF.uniq((CONFIG.defaultLabels || []).concat(collectAllLabels())),
|
|
470
383
|
model: CONFIG.ai.model,
|
|
471
384
|
maxTokens: CONFIG.ai.maxTokens
|
|
472
385
|
};
|
|
@@ -570,8 +483,19 @@
|
|
|
570
483
|
}
|
|
571
484
|
}
|
|
572
485
|
|
|
573
|
-
|
|
486
|
+
/**
|
|
487
|
+
* Open the dialog.
|
|
488
|
+
* @param {string|{type?:string, description?:string, extra?:string}} opts
|
|
489
|
+
* A bare type id is still accepted — window.zer0PageFeedback.open('fix-page')
|
|
490
|
+
* is a published surface — but the object form is what other components use
|
|
491
|
+
* to hand over context the reader should not have to retype: the 404 page
|
|
492
|
+
* passes the URL that failed, an error boundary would pass the stack.
|
|
493
|
+
*/
|
|
494
|
+
function open(opts) {
|
|
574
495
|
if (state.open) return;
|
|
496
|
+
if (typeof opts === "string") opts = { type: opts };
|
|
497
|
+
opts = opts || {};
|
|
498
|
+
var preselectType = opts.type || null;
|
|
575
499
|
state.open = true;
|
|
576
500
|
state.lastFocus = document.activeElement;
|
|
577
501
|
modal.hidden = false;
|
|
@@ -584,6 +508,9 @@
|
|
|
584
508
|
state.analysis = null;
|
|
585
509
|
renderContext();
|
|
586
510
|
|
|
511
|
+
if (opts.description && els.description) {
|
|
512
|
+
els.description.value = opts.description;
|
|
513
|
+
}
|
|
587
514
|
var type = preselectType || state.typeId;
|
|
588
515
|
if (type && typeById(type)) {
|
|
589
516
|
setType(type);
|
|
@@ -591,6 +518,7 @@
|
|
|
591
518
|
} else if (els.types.length) {
|
|
592
519
|
els.types[0].focus();
|
|
593
520
|
}
|
|
521
|
+
updateValidity();
|
|
594
522
|
document.addEventListener("keydown", onKeydown, true);
|
|
595
523
|
|
|
596
524
|
if (CONFIG.ai && CONFIG.ai.available && CONFIG.ai.auto && state.typeId) analyze();
|
|
@@ -644,12 +572,17 @@
|
|
|
644
572
|
if (els.contextCount) els.contextCount.textContent = "(" + (base + withLogs) + ")";
|
|
645
573
|
}
|
|
646
574
|
|
|
647
|
-
// Any [data-pf-open] element opens the modal; data-pf-type preselects a type
|
|
575
|
+
// Any [data-pf-open] element opens the modal; data-pf-type preselects a type
|
|
576
|
+
// and data-pf-description pre-fills the textarea (the 404 page sets it to the
|
|
577
|
+
// URL that failed just before the click reaches here).
|
|
648
578
|
document.addEventListener("click", function (event) {
|
|
649
579
|
var trigger = event.target.closest ? event.target.closest("[data-pf-open]") : null;
|
|
650
580
|
if (!trigger) return;
|
|
651
581
|
event.preventDefault();
|
|
652
|
-
open(
|
|
582
|
+
open({
|
|
583
|
+
type: trigger.getAttribute("data-pf-type") || null,
|
|
584
|
+
description: trigger.getAttribute("data-pf-description") || ""
|
|
585
|
+
});
|
|
653
586
|
});
|
|
654
587
|
|
|
655
588
|
// Update the submit verb to match the configured mode.
|