-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
251 lines (215 loc) · 10.5 KB
/
Copy pathscript.js
File metadata and controls
251 lines (215 loc) · 10.5 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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
const navToggle = document.querySelector(".nav-toggle");
const navLinks = document.querySelector(".nav-links");
if (navToggle && navLinks) {
navToggle.addEventListener("click", () => {
const isOpen = navLinks.classList.toggle("is-open");
navToggle.setAttribute("aria-expanded", String(isOpen));
});
}
const topButton = document.querySelector("[data-to-top]");
if (topButton) {
window.addEventListener("scroll", () => {
topButton.classList.toggle("is-visible", window.scrollY > 520);
});
topButton.addEventListener("click", () => {
window.scrollTo({ top: 0, behavior: "smooth" });
});
}
const plannerForm = document.querySelector("[data-kitchen-planner]");
const plannerOutput = document.querySelector("[data-kitchen-output]");
const plannerData = {
leafy: {
ingredients: "leafy greens and herbs",
note: "Use tender leaves first. Keep a clear discard rule for wilted or unsafe produce."
},
fruit: {
ingredients: "ripe fruit",
note: "Sort ripe fruit today, wash only what will be used, and keep bruised fruit separate."
},
fish: {
ingredients: "fresh fish or seafood",
note: "This needs a strict cold-chain check, clear timing and food-safe handling."
},
pantry: {
ingredients: "pantry staples",
note: "Check best-before dates, packaging condition and allergen labels before sharing."
}
};
const escapeHtml = (value) => String(value)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
if (plannerForm && plannerOutput) {
const updatePlanner = () => {
const formData = new FormData(plannerForm);
const surplus = formData.get("surplus") || "leafy";
const timing = formData.get("timing") || "flexible";
const mood = formData.get("mood") || "warm";
const data = plannerData[surplus];
const timingText = {
flexible: "flexible through the day",
breakfast: "breakfast",
brunch: "brunch",
lunch: "lunch",
afternoon: "afternoon or after-school",
evening: "evening",
late: "late or shift-worker friendly"
}[timing] || "flexible through the day";
const moodText = {
warm: "warm, simple and family-friendly",
quick: "quick, practical and low-mess",
youth: "colourful, social and easy to film",
elder: "slow, respectful and story-led"
}[mood];
const prompt = `I have ${data.ingredients}, plus this pantry/freezer/garden list: [add list]. We are planning a ${timingText} shared table for [number] people while fuel and freight pressure are making food more expensive. Make it ${moodText}. Suggest a simple meal plan using mostly what we have, a short essential shopping gap list, a prep/preserving roster, allergy questions, food safety checks, storage notes, and what to do with leftovers. Keep the tone practical, calm and non-judgemental.`;
plannerOutput.innerHTML = `
<h3>Starter prompt</h3>
<p>${escapeHtml(data.note)}</p>
<pre class="generated-prompt" data-copy-source>${escapeHtml(prompt)}</pre>
<button class="copy-button" type="button" data-copy-output>Copy prompt</button>
`;
};
plannerForm.addEventListener("change", updatePlanner);
updatePlanner();
}
const gardenForm = document.querySelector("[data-garden-planner]");
const gardenOutput = document.querySelector("[data-garden-output]");
const gardenSpaces = {
backyard: "a backyard or side yard",
balcony: "a balcony, deck or rental-friendly pot setup",
school: "a school, club or community garden plot",
indoor: "an indoor shelf, bench or microgreens setup"
};
const gardenFocuses = {
quick: "quick herbs and leafy greens for everyday meals",
staple: "filling staple crops that suit the space and season",
preserve: "crops that can be frozen, dried, pickled, cooked down or shared before spoilage",
native: "native or local food plants only where permission, knowledge and safety are clear"
};
const gardenMaintenance = {
low: "low-maintenance care that can survive busy weeks",
shared: "a shared roster with small jobs for several people",
learning: "a learning project for kids, elders and new growers",
intensive: "a higher-yield setup without pretending everyone has unlimited time"
};
if (gardenForm && gardenOutput) {
const updateGarden = () => {
const formData = new FormData(gardenForm);
const space = formData.get("space") || "backyard";
const focus = formData.get("focus") || "quick";
const maintenance = formData.get("maintenance") || "low";
const prompt = `We have ${gardenSpaces[space]}, and we want to grow ${gardenFocuses[focus]}. Design a practical Garden Mate plan for Straddie / coastal south-east Queensland conditions. Assume ${gardenMaintenance[maintenance]}. Include what to grow first, setup steps, watering and shade notes, weekly jobs, likely harvest timing, pest and heat risks, what to do if the plan starts failing, and how to post surplus to a private Shared Table board without exposing private addresses or cultural knowledge. Keep it grounded and low-waste.`;
gardenOutput.innerHTML = `
<h3>Garden Mate starter prompt</h3>
<p>Use this as a first pass, then adjust it with local observation and real grower knowledge.</p>
<pre class="generated-prompt" data-copy-source>${escapeHtml(prompt)}</pre>
<button class="copy-button" type="button" data-copy-output>Copy prompt</button>
`;
};
gardenForm.addEventListener("change", updateGarden);
updateGarden();
}
const boardBuilder = document.querySelector("[data-board-builder]");
const boardOutput = document.querySelector("[data-board-output]");
const boardTypes = {
produce: "We have [produce / pantry / freezer item] available. Who can use it, add to it, preserve it, or help turn it into a simple shared meal?",
meal: "We are planning a small shared meal from what the group already has. Reply with what you can bring, what timing suits, and any allergy or storage notes.",
roster: "We need a few small jobs covered: pickup, prep, cooking, containers, delivery, cleanup and leftovers. Pick one job that fits your day.",
preserve: "We have surplus [produce / cooked food / pantry item] that needs action. Who can help freeze, dry, preserve, cook, share, label containers, or compost what cannot be used?",
care: "Quiet care check-in: is anyone short on meals this week, overloaded, or needing a no-fuss drop-off? Reply privately if that feels easier."
};
const timingLines = {
flexible: "Timing is flexible through the day. Morning, brunch, afternoon, evening and late pickup are all valid.",
morning: "Morning prep or breakfast timing preferred, but say if another time works better.",
brunch: "Brunch timing is welcome. Think late morning, easy prep, and food that can stretch into lunch if needed.",
midday: "Midday meal timing is the starting point, with room for earlier prep or later pickup.",
afternoon: "After-school or afternoon timing is the starting point. Keep jobs small and realistic.",
evening: "Evening cook-up timing is the starting point. Late pickup can be arranged inside the trusted group.",
shift: "Shift-worker friendly timing is welcome. Early pickup, late pickup or split prep can all count."
};
const toneLines = {
neighbour: "Keep it neighbourly, relaxed and clear.",
club: "Keep it organised, practical and easy for a club kitchen to follow.",
care: "Keep it gentle, private and low-pressure.",
youth: "Keep it direct, friendly and easy for younger helpers to act on."
};
if (boardBuilder && boardOutput) {
const updateBoard = () => {
const formData = new FormData(boardBuilder);
const type = formData.get("type") || "produce";
const timing = formData.get("timing") || "flexible";
const tone = formData.get("tone") || "neighbour";
const message = `${boardTypes[type]}\n\n${timingLines[timing]}\n\n${toneLines[tone]}\n\nPlease include: what you are offering or need, when it expires, allergy or storage notes, who is responsible, and whether any part must stay private.`;
boardOutput.innerHTML = `
<h3>Private board draft</h3>
<pre class="generated-prompt" data-copy-source>${escapeHtml(message)}</pre>
<button class="copy-button" type="button" data-copy-output>Copy message</button>
`;
};
boardBuilder.addEventListener("change", updateBoard);
updateBoard();
}
const noticeButtons = document.querySelectorAll("[data-notice-choice]");
const noticePreview = document.querySelector("[data-notice-preview]");
const noticeTemplates = {
surplus: `---
title: Surplus produce call-out
status: draft
visibility: public
expires: add date before publishing
privacy_check: no private addresses or phone numbers
---
The Shared Table is checking what surplus produce is available this week.
Share only food that is safe, labelled and ready for review by the table crew.`,
volunteer: `---
title: Shared Table volunteer shift
status: draft
visibility: public
expires: add date before publishing
privacy_check: roster privately, publish only the public call-out
---
We need a small crew for setup, washing, serving and pack-down.
New helpers are welcome. A local guide can help with the first shift.`,
safety: `---
title: Food safety gate update
status: draft
visibility: public
expires: add date before publishing
privacy_check: do not name donors without consent
---
This week's table only uses ingredients that pass the basic safety gate.
Foraged or unusual foods stay out until the correct checks and permissions are complete.`
};
if (noticeButtons.length && noticePreview) {
const setNotice = (type) => {
noticePreview.textContent = noticeTemplates[type];
noticeButtons.forEach((button) => {
button.setAttribute("aria-pressed", String(button.dataset.noticeChoice === type));
});
};
noticeButtons.forEach((button) => {
button.addEventListener("click", () => setNotice(button.dataset.noticeChoice));
});
setNotice("surplus");
}
document.addEventListener("click", async (event) => {
const button = event.target.closest("[data-copy-prompt], [data-copy-output]");
if (!button) return;
const promptText = button.closest(".prompt-card")?.querySelector("p")?.textContent
|| button.closest(".output-card")?.querySelector("[data-copy-source]")?.textContent;
if (!promptText) return;
try {
await navigator.clipboard.writeText(promptText.trim());
const originalText = button.textContent;
button.textContent = "Copied";
button.classList.add("is-copied");
window.setTimeout(() => {
button.textContent = originalText;
button.classList.remove("is-copied");
}, 1400);
} catch (error) {
button.textContent = "Select text";
}
});