mirror of
https://github.com/arfct/itty-bitty.git
synced 2026-03-11 08:54:33 +00:00
Update Recipe formatting
This commit is contained in:
parent
64d15d9049
commit
df2bd0c682
5 changed files with 157 additions and 47 deletions
31
.vscode/launch.json
vendored
Normal file
31
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "netlify dev",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"outFiles": ["${workspaceFolder}/.netlify/functions-serve/**/*.js"],
|
||||
"program": "${workspaceFolder}/node_modules/.bin/netlify",
|
||||
"args": ["dev"],
|
||||
"console": "integratedTerminal",
|
||||
"env": { "BROWSER": "none" },
|
||||
"serverReadyAction": {
|
||||
"pattern": "Server now ready on (https?://[\w:.-]+)",
|
||||
"uriFormat": "%s",
|
||||
"action": "debugWithChrome"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "netlify functions:serve",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"outFiles": ["${workspaceFolder}/.netlify/functions-serve/**/*.js"],
|
||||
"program": "${workspaceFolder}/node_modules/.bin/netlify",
|
||||
"args": ["functions:serve"],
|
||||
"console": "integratedTerminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
9
.vscode/settings.json
vendored
Normal file
9
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"deno.enable": true,
|
||||
"deno.enablePaths": [
|
||||
"netlify/edge-functions"
|
||||
],
|
||||
"deno.unstable": true,
|
||||
"deno.importMap": ".netlify/edge-functions-import-map.json",
|
||||
"deno.path": "~/Library/Preferences/netlify/deno-cli/deno"
|
||||
}
|
||||
|
|
@ -176,7 +176,7 @@ ul.step {
|
|||
|
||||
ul.step:after {
|
||||
content: "";
|
||||
display: block;
|
||||
display: none;
|
||||
width: 5em;
|
||||
height: 0.5px;
|
||||
background-color: currentColor;
|
||||
|
|
@ -198,8 +198,12 @@ ul.step:last-child:after {
|
|||
display: none;
|
||||
}
|
||||
|
||||
span.number {
|
||||
margin-left: -3rem;
|
||||
.instructions.numbered .step {
|
||||
margin-bottom:1em;
|
||||
}
|
||||
|
||||
span.bullet {
|
||||
margin-left: -1.5rem;
|
||||
float:
|
||||
left;
|
||||
/* border: 1.5px solid var(--text-color); */
|
||||
|
|
@ -215,11 +219,18 @@ span.number {
|
|||
color:
|
||||
var(--accent-color)
|
||||
/* background: black; */;
|
||||
display: none;
|
||||
/* display: none; */
|
||||
/* display: block; */
|
||||
}
|
||||
|
||||
.instructions .step:not(:hover) span.bullet.substep {
|
||||
opacity: 0.3;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
|
||||
@media screen {
|
||||
.complete span.number, li:hover span.number {
|
||||
.complete span.bullet, li:hover span.bullet {
|
||||
color:transparent;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='48' width='48'%3E%3Cpath d='M18.9 36.9 6.5 24.5 9.9 21.1 18.9 30.1 38.05 10.95 41.45 14.35Z'/%3E%3C/svg%3E"); background-size:cover;
|
||||
|
||||
|
|
@ -343,8 +354,8 @@ li:hover .noun {
|
|||
}
|
||||
|
||||
.instructions.numbered li {
|
||||
/* padding-left: 3rem; */
|
||||
/* margin-left: 0; */
|
||||
padding-left: 2rem;
|
||||
margin-left: -0.6rem;
|
||||
}
|
||||
|
||||
.substep {
|
||||
|
|
@ -352,16 +363,16 @@ li:hover .noun {
|
|||
/* display: inline; */
|
||||
}
|
||||
.ingredient {
|
||||
padding: .4em 0.4em;
|
||||
margin: 0 -.4em 1em -.4em;
|
||||
line-height: 1em;
|
||||
/* padding: .4em 0.4em; */
|
||||
/* margin: 0 -.4em 1em -.4em; */
|
||||
line-height: 120%;
|
||||
/* opacity: 0.75; */
|
||||
cursor: pointer;
|
||||
margin-bottom:0.2em;
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
.hanging .ingredient {
|
||||
margin-left: -3rem;
|
||||
padding-left: 3rem;
|
||||
/* margin-left: -3rem; */
|
||||
/* padding-left: 3rem; */
|
||||
}
|
||||
|
||||
.ingredients .yield {
|
||||
|
|
@ -371,14 +382,15 @@ li:hover .noun {
|
|||
margin-left:2em;
|
||||
}
|
||||
.ingredients.hanging .quantity {
|
||||
float:
|
||||
left;
|
||||
margin-left: -3rem;
|
||||
min-width: 3rem;
|
||||
/* float:
|
||||
left; */
|
||||
/* margin-left: -3rem; */
|
||||
/* max-width: 1rem; */
|
||||
text-align:
|
||||
right;
|
||||
color: var(--accent-color);
|
||||
/* font-weight: bold; */
|
||||
width: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ingredient:hover {
|
||||
|
|
@ -536,8 +548,7 @@ hr {
|
|||
|
||||
img.qr {
|
||||
/* margin-top:2em; */
|
||||
position:
|
||||
absolute;
|
||||
position: absolute;
|
||||
bottom:0;
|
||||
left: 0;
|
||||
max-width: 1in;
|
||||
|
|
@ -685,7 +696,8 @@ span.number:after {
|
|||
|
||||
section.ingredients caption {
|
||||
content: "Ingredients";
|
||||
margin-bottom: 1.3rem;
|
||||
display: table-caption;
|
||||
margin-bottom: calc(1.3rem - 1em);
|
||||
}
|
||||
|
||||
.hanging .ingredient .quantity:after {
|
||||
|
|
@ -697,20 +709,25 @@ section.ingredients caption {
|
|||
/* padding-right: 24px; */
|
||||
margin-bottom: 1.3rem;
|
||||
}
|
||||
|
||||
.ingredients caption, .instructions caption {
|
||||
text-align:
|
||||
left;
|
||||
a.publisherlink,
|
||||
.ingredients caption,
|
||||
.instructions caption {
|
||||
font-weight: 600;
|
||||
margin-bottom: 1em;
|
||||
display: block;
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.01rem;
|
||||
user-select: none;
|
||||
/* margin: 0; */
|
||||
color: var(--accent-color);
|
||||
}
|
||||
.ingredients caption,
|
||||
.instructions caption {
|
||||
text-align: left;
|
||||
display: block;
|
||||
margin-bottom: 1em;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
.listtoggle {
|
||||
/* float: right; */
|
||||
|
|
@ -730,6 +747,8 @@ a.publisherlink {
|
|||
/* order: -2; */
|
||||
/* width: 100%; */
|
||||
float: left;
|
||||
text-decoration: none;
|
||||
color: var(--disabled-text-color);
|
||||
}
|
||||
|
||||
.headerleft h1 {
|
||||
|
|
@ -779,3 +798,37 @@ span.timer.active.expired {
|
|||
}
|
||||
}
|
||||
|
||||
.ingredients {
|
||||
|
||||
}
|
||||
|
||||
|
||||
.ingredients {
|
||||
display: table;
|
||||
width: auto;
|
||||
/* background-color: #eee; */
|
||||
/* border: 1px solid #666666; */
|
||||
border-spacing: 5px; /* cellspacing:poor IE support for this */
|
||||
/* border-collapse: separate; */
|
||||
border-spacing: 0 1em;
|
||||
height: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
.ingredient {
|
||||
display: table-row;
|
||||
width: auto;
|
||||
clear: both;
|
||||
}
|
||||
.ingredient > * {
|
||||
/* float: left; */ /* fix for buggy browsers */
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
/* background-color: red; */
|
||||
/* padding: 0; */
|
||||
/* margin: 0; */
|
||||
/* margin-bottom: 1em; */
|
||||
}
|
||||
|
||||
a.publisherlink:hover {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ let FRACTION_MAP = {
|
|||
}
|
||||
|
||||
let ignoredTerms = [
|
||||
"broken", "pieces", "tbsp", "tsp", "peeled", "then", "can", "oz", "fresh", "out", "not", "sprig", "sprigs", "room", "temperature", "still", "see", "notes", "with", "beat", "together", "crust", "very", "cold", "hot", "top", "warm", "one", "note", "teaspoon", "teaspoons", "tablespoon", "tablespoons", "cup", "cups", "taste", "more", "melted", "into", "wide", "pound", "pounds", "gram", "grams", "you", "ounce", "ounces", "thinly", "sliced",
|
||||
"but", "broken", "pieces", "tbsp", "tsp", "peeled", "then", "can", "oz", "fresh", "out", "not", "sprig", "sprigs", "room", "temperature", "still", "see", "notes", "with", "beat", "together", "crust", "very", "cold", "hot", "top", "warm", "one", "note", "teaspoon", "teaspoons", "tablespoon", "tablespoons", "cup", "cups", "taste", "more", "melted", "into", "wide", "pound", "pounds", "gram", "grams", "you", "ounce", "ounces", "thinly", "sliced",
|
||||
"pan", "cube", "cubes", "finely", "ground", "garnish", "about", "cut", "and", "smashed", "each", "the", "medium", "large", "small", "for", "chopped", "minced", "grated", "box", "softened", "directed", "shredded", "cooked", "from", "frozen", "thawed"
|
||||
]
|
||||
let emojiMap = {
|
||||
|
|
@ -294,10 +294,11 @@ const ingredientMatch = /^(?:A )?([\-\/0-9 \u00BC-\u00BE\u2153-\u215E\u2009]*)\s
|
|||
function ingredientEl(string, terms) {
|
||||
if (string == "-") return m("hr");
|
||||
|
||||
string = highlightTerms(string, terms)
|
||||
// string = highlightTerms(string, terms)
|
||||
//console.log("terms", terms);
|
||||
|
||||
let match = FRACTION_MAP.replace(clean(string)).match(ingredientMatch);
|
||||
// let emoji = faviconForTitle(string);
|
||||
|
||||
if (match) {
|
||||
return [m("span.quantity", match[1].replace(" ", "\u202F")), " ", m("span", {innerHTML:highlightTerms(match[2], terms)})]
|
||||
|
|
@ -306,8 +307,11 @@ function ingredientEl(string, terms) {
|
|||
}
|
||||
|
||||
function highlightTerms(string, terms) {
|
||||
const pattern = new RegExp(`\\b(${Array.from(terms).join('|').replace("-","\\-") })\\b`, 'gi');
|
||||
return string.replace(pattern, match => `<span class="noun" id="term-${match}">${match}</span>`);
|
||||
const pattern = new RegExp(`\\b((${Array.from(terms).join('|').replace("-","\\-")}))\\b`, 'gi');
|
||||
return string.replace(pattern, match => {
|
||||
let emoji = undefined //faviconForTitle(match);
|
||||
return `<span class="noun" id="term-${match.trim().replace(" ","-")}">${ emoji ? emoji + " " : ""}${match}</span>`
|
||||
});
|
||||
}
|
||||
|
||||
function highlightTimes(string) {
|
||||
|
|
@ -420,10 +424,11 @@ function render() {
|
|||
let ingredients = json.recipeIngredient;
|
||||
|
||||
var ingredientTerms = new Set(
|
||||
Array.from(ingredients.join("\n").matchAll(/[A-Za-z\-]+/g)).map(m => m[0].length > 2 ? m[0].toLowerCase(): "")
|
||||
Array.from(ingredients.join("\n").matchAll(/(\p{L}\p{M}?)+/gu)).map(m => m[0].length > 2 ? m[0].toLowerCase(): "")
|
||||
);
|
||||
if (typeof instructions === "string") instructions = [instructions]
|
||||
instructions = flattenInstructions(instructions)
|
||||
console.log("instructions", instructions)
|
||||
let intructionTerms = new Set(
|
||||
Array.from(instructions.flat().join("\n").matchAll(/[A-Za-z\-]+/g)).map(m => m[0].length > 2 ? m[0].toLowerCase(): "")
|
||||
);
|
||||
|
|
@ -443,12 +448,14 @@ function render() {
|
|||
ingredients = ingredients.map(i => m("div.ingredient", { onclick: markIngredient }, ingredientEl(clean(i), ingredientTerms)));
|
||||
|
||||
let step = 1;
|
||||
function renderInstructions(instruction, terms) {
|
||||
function renderInstructions(instruction, terms, i) {
|
||||
if (Array.isArray(instruction)) {
|
||||
let instructions = instruction.map(i => renderInstructions(i, terms));
|
||||
let instructions = instruction.map((inst,i) => renderInstructions(inst, terms, i));
|
||||
let className = "step";
|
||||
if (instructions[0].tagName=="H3") className = "step header"
|
||||
return m("ul", {className}, instructions);
|
||||
|
||||
if (instructions[0].tagName=="H3") className = "step header";
|
||||
let number = undefined // m("div.number", {}, "" + step++)
|
||||
return m("ul", {className}, number, instructions);
|
||||
}
|
||||
|
||||
let text = (instruction?.text || instruction);
|
||||
|
|
@ -458,7 +465,9 @@ function render() {
|
|||
if (text?.endsWith(":")) return m("h3", text);
|
||||
|
||||
return m("li", { onclick: highlightStep },
|
||||
m("span.number" + (step > 9 ? ".big" : ""), `${step++}`),
|
||||
i == 0 ?
|
||||
m("span.bullet.number" + (step > 9 ? ".big" : ""), (step++).toString()) :
|
||||
m("span.bullet.substep", "·"),
|
||||
m("span.substep",{innerHTML:highlightTimes(highlightTerms(FRACTION_MAP.replace(text.trim()), terms))}))
|
||||
}
|
||||
|
||||
|
|
@ -489,26 +498,25 @@ function render() {
|
|||
}
|
||||
if (end) {
|
||||
let string = text.substring(start, end);
|
||||
steps.push(string);
|
||||
steps.push(string.trim());
|
||||
start = end;
|
||||
}
|
||||
break;
|
||||
case "\n":
|
||||
steps.push(text.substring(start, i));
|
||||
steps.push(text.substring(start, i).trim());
|
||||
start = i;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
steps.push (text.substring(start));
|
||||
console.log("step", steps)
|
||||
|
||||
return steps;
|
||||
|
||||
}
|
||||
function flattenInstructions(instruction) {
|
||||
if (instruction.itemListElement) {
|
||||
return ["= " + instruction.name].concat(flattenInstructions(instruction.itemListElement).flat());
|
||||
return ["= " + instruction.name].concat(flattenInstructions(instruction.itemListElement));
|
||||
}
|
||||
|
||||
if (Array.isArray(instruction)) {
|
||||
|
|
@ -528,8 +536,9 @@ function render() {
|
|||
return text;
|
||||
}
|
||||
|
||||
instructions = instructions.map(i => renderInstructions(i, ingredientTerms));
|
||||
if (instructions.length == 1) console.log("One instruction")
|
||||
|
||||
instructions = instructions.map(inst => renderInstructions(inst, ingredientTerms));
|
||||
|
||||
|
||||
// instructions = instructions.map(i => m("div.step", { onclick: highlightStep }, i))
|
||||
|
|
@ -554,6 +563,7 @@ function render() {
|
|||
var bgImg = new Image();
|
||||
bgImg.onload = function(){
|
||||
let thumbnail = document.querySelector("#thumbnail");
|
||||
if (!thumbnail) return;
|
||||
let thumbnailContainer = document.querySelector("#thumbnail-container");
|
||||
thumbnail.style.backgroundImage = 'url("' + bgImg.src + '")';
|
||||
|
||||
|
|
@ -573,15 +583,19 @@ function render() {
|
|||
|
||||
console.log("image", bgImg.src)
|
||||
|
||||
let originalURL = json.mainEntityOfPage?.["@id"] ?? ((json.mainEntityOfPage == true) ? false : json.mainEntityOfPage) ?? json.url;
|
||||
console.log({originalURL})
|
||||
let hostname = originalURL ? new URL(originalURL).hostname.replace("www.","") : ""
|
||||
let qrImage = QRCodeURL(params.originalURL, {margin:0});
|
||||
let originalURL = json.mainEntityOfPage?.["@id"] || json.mainEntityOfPage || json.url;
|
||||
let publisherImage = json.publisher?.image ?.[0]?.url ?? json.publisher ?.logo ?.url;
|
||||
|
||||
document.body.appendChild(
|
||||
m(".recipe", {},
|
||||
image ? m("#thumbnail-container", m("#thumbnail.thumbnail.print-hide", { style: "background-image:url(" + image + ");" })) : null,
|
||||
m(".recipe-content",
|
||||
m("header",
|
||||
m("a.publisherlink", {href:originalURL, target:"_blank"},
|
||||
m("img.publisher", { src: json.publisher?.image ?.[0]?.url ?? json.publisher ?.logo ?.url }),
|
||||
publisherImage ? m("img.publisher", { src: publisherImage }) : hostname,
|
||||
),
|
||||
m(".headerflex",
|
||||
m(".headerleft",
|
||||
|
|
@ -630,7 +644,7 @@ function render() {
|
|||
qrImage ? m("img#qr.qr.print-show", {src:qrImage}) : null,
|
||||
// m("canvas#qr")
|
||||
),
|
||||
m(reformat ? "section.instructions.numbered" : "section.instructions",
|
||||
m(reformat ? "section.instructions.numbered" : "section.instructions.numbered",
|
||||
m("caption.ingredients-title", {onclick:() => {reformat = !reformat; render(); return false;}},
|
||||
reformat ? "Steps" : "Instructions",
|
||||
m("div.listtoggle.print-hide", {innerHTML: '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" viewBox="0 0 16 16"><mask id="a" width="16" height="16" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#D9D9D9" d="M0 0h16v16H0z"/></mask><g mask="url(#a)"><path fill="#000" d="M3.7 12.667 1.333 10.3l.934-.933 1.416 1.416L6.517 7.95l.933.95-3.75 3.767Zm0-5.334L1.333 4.967l.934-.934L3.683 5.45l2.834-2.833.933.95L3.7 7.333Zm4.967 4V10h6v1.333h-6Zm0-5.333V4.667h6V6h-6Z"/></g></svg>'}),
|
||||
|
|
@ -671,7 +685,7 @@ function keepAwake() {
|
|||
var path = window.script.substring(0, window.script.lastIndexOf("."));
|
||||
var cssURL = path + ".css";
|
||||
loadScript(path + '/../../js/qrious.min.js', null, "").then(() => {
|
||||
console.log("qrious loaded");
|
||||
console.log("qrious loaded", params.originalURL.length);
|
||||
var qr = new QRious({
|
||||
element: document.getElementById("qr"),
|
||||
background: 'transparent',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"brotli-wasm": "^1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"netlify-cli": "^11.5.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue