From 2006e74233a3d4225f81cc4b4c487b24f4c23453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juli=C3=A1n=20M=C3=A9ndez?= Date: Thu, 28 Nov 2024 13:59:13 +0100 Subject: [PATCH] refactor .spy views, unbind free variables from axes and visualize no intersections on no solutions --- .../public/js/proof/rules/cd/linear/linear.js | 129 +++++------------- frontend/public/style/colors.css | 3 + frontend/public/style/inference.css | 2 +- frontend/public/style/ontology.css | 6 +- frontend/public/style/proof.css | 2 +- frontend/public/style/widgets/explanation.css | 8 +- frontend/public/style/widgets/general.css | 2 + frontend/views/defs.spy | 12 -- frontend/views/euler/euler.spy | 4 +- frontend/views/inference/inference.spy | 4 +- frontend/views/ontology/ontology.spy | 31 ----- frontend/views/{main => pages}/demo.spy | 4 +- .../views/{main/split.spy => pages/main.spy} | 10 +- frontend/views/{main => pages}/test.spy | 0 .../{main/main.spy => pages/welcome.spy} | 6 +- frontend/views/proof/proof.spy | 31 ----- frontend/views/scripts.spy | 17 +-- frontend/views/styles.spy | 4 - .../examples}/examples-eurovis.spy | 0 .../examples}/examples-ijcar.spy | 0 .../views/{ => widgets}/menus/compute.spy | 0 frontend/views/{ => widgets}/menus/menu.spy | 0 .../views/{ => widgets}/menus/projects.spy | 0 .../views/{ => widgets}/menus/settings.spy | 0 .../views/{ => widgets}/menus/shortening.spy | 0 .../modals/advanced-settings.spy | 0 frontend/views/{ => widgets}/modals/axiom.spy | 2 +- .../{ => widgets}/modals/file-upload.spy | 0 frontend/views/{main => widgets}/progress.spy | 0 server.js | 56 ++------ 30 files changed, 81 insertions(+), 252 deletions(-) delete mode 100644 frontend/views/defs.spy delete mode 100644 frontend/views/ontology/ontology.spy rename frontend/views/{main => pages}/demo.spy (96%) rename frontend/views/{main/split.spy => pages/main.spy} (78%) rename frontend/views/{main => pages}/test.spy (100%) rename frontend/views/{main/main.spy => pages/welcome.spy} (96%) delete mode 100644 frontend/views/proof/proof.spy rename frontend/views/{main => widgets/examples}/examples-eurovis.spy (100%) rename frontend/views/{main => widgets/examples}/examples-ijcar.spy (100%) rename frontend/views/{ => widgets}/menus/compute.spy (100%) rename frontend/views/{ => widgets}/menus/menu.spy (100%) rename frontend/views/{ => widgets}/menus/projects.spy (100%) rename frontend/views/{ => widgets}/menus/settings.spy (100%) rename frontend/views/{ => widgets}/menus/shortening.spy (100%) rename frontend/views/{ => widgets}/modals/advanced-settings.spy (100%) rename frontend/views/{ => widgets}/modals/axiom.spy (99%) rename frontend/views/{ => widgets}/modals/file-upload.spy (100%) rename frontend/views/{main => widgets}/progress.spy (100%) diff --git a/frontend/public/js/proof/rules/cd/linear/linear.js b/frontend/public/js/proof/rules/cd/linear/linear.js index 17bacb8..6f8c6d9 100644 --- a/frontend/public/js/proof/rules/cd/linear/linear.js +++ b/frontend/public/js/proof/rules/cd/linear/linear.js @@ -23,6 +23,7 @@ const tip = { const premiseColor = '#80cbc4'; const conclusionColor = '#b89aef'; const plot = 'cd-linear-plot'; +const eval_tolerance = 0.001; let struct = {}; // copy of the node/subproof data let vars = []; // list of variables. The order of this list determines the order of the solutions, which allows us to "target" which variables will be free in case of infinite solutions. @@ -192,19 +193,12 @@ function vis(plot, solution) { container.appendChild(vis); } - vis.innerHTML = - `
+ vis.innerHTML = `
`; - `; + return visByType(solution); +} +function visByType(solution) { switch (solution.type) { case 'unique solution': return visualizeUniqueSolution(plot, solution); @@ -264,7 +258,9 @@ function visualizeUniqueSolution(plot, _data) { annotations.push({ x: eval(f(v)), color }); hl[id] = { svg: () => document.querySelectorAll(`#${plot} .annotations path`)[annotations.length-1], - evaluate: point => Fraction(v).equals(Fraction(point.x)), + evaluate: point => + Fraction(v).gt(Fraction(point.x - eval_tolerance)) && + Fraction(v).lt(Fraction(point.x + eval_tolerance)), color }; annots += 1; @@ -279,7 +275,7 @@ function visualizeUniqueSolution(plot, _data) { const p = Fraction(independentTerm).mul(Fraction(point.x)); const s = Fraction(c).sub(p); const d = s.div(Fraction(dependentCoefficient)); - return d.equals(Fraction(point.y)); + return d.gt(Fraction(point.y - eval_tolerance)) && d.lt(Fraction(point.y + eval_tolerance)); }, color }; @@ -405,8 +401,11 @@ function visualizeInfiniteSolutions(plot, _data) { } } - const x = { varName: vars[0], fn: dependentValues[vars[0]] }; - const y = { varName: vars[1], fn: dependentValues[vars[1]] }; + const select1 = document.querySelector(`#var1`); + const select2 = document.querySelector(`#var2`); + + const x = { varName: select1.value, fn: dependentValues[select1.value] || independent[select1.value]}; + const y = { varName: select2.value, fn: dependentValues[select2.value] || independent[select2.value] }; const annotations = [ { x: eval(f(x.fn)), text: `${x.varName} = ${f(x.fn)}` }, @@ -457,24 +456,27 @@ function visualizeInfiniteSolutions(plot, _data) { annotations.push({ x: eval(f(v)), text: `${x.varName} = ${f(v)}`, color }); hl[id] = { svg: () => document.querySelectorAll(`#${plot} .annotations path`)[annotations.length-1], - evaluate: point => Fraction(v).equals(Fraction(point.x)), + evaluate: point => + Fraction(v).gt(Fraction(point.x - eval_tolerance)) && + Fraction(v).lt(Fraction(point.x + eval_tolerance)), color }; annots += 1; } else { - console.error('both of the selected variables canceled'); + console.log(row.map(r => f(r))) + console.log('both of the selected variables canceled -- no intersection'); } } else { data.push({ fn: equation, color, updateOnMouseMove: false, graphType: 'polyline' }); hl[id] = { + color, svg: () => document.querySelectorAll(`#${plot} .graph path.line`)[Math.max(0, i - annots)], evaluate: (point) => { const p = Fraction(independentCoef).mul(Fraction(point.x)).add(Fraction(replacedSum)); const s = Fraction(constantTerm).sub(p); const d = s.div(Fraction(dependentCoef)); - return d.equals(Fraction(point.y)); - }, - color + return d.gt(Fraction(point.y - eval_tolerance)) && d.lt(Fraction(point.y + eval_tolerance)); + } }; } } @@ -503,34 +505,8 @@ function visualizeInfiniteSolutions(plot, _data) { return { plot: p, hl }; } -function visualizeNoSolutions(plot, data) { - const initialStatePreview = document.getElementById(`${plot}-initialStatePreview`); - const finalStatePreview = document.getElementById(`${plot}-finalStatePreview`); - const noSolutionPreviewContainer = document.getElementById(`${plot}-noSolutionPreviewContainer`); - - // Function to render matrix using KaTeX - function renderMatrixKaTeX(matrix, container) { - let matrixString = '\\begin{bmatrix}'; - matrix.forEach((row, idx) => { - matrixString += row.slice(0, -1).map(coef => f(coef)).join(' & '); - matrixString += ' & \\vert & ' + f(row[row.length - 1]); // Adds the augmented column - if (idx < matrix.length - 1) matrixString += ' \\\\ '; - }); - matrixString += '\\end{bmatrix}'; - - // Clear previous content and render new matrix - container.innerHTML = ''; - katex.render(matrixString, container, { throwOnError: false, displayMode: true }); - } - - // Render initial and final state matrices - renderMatrixKaTeX(data.matrix.slice(0, data.matrix.length - 1), initialStatePreview); - renderMatrixKaTeX(data.echelon, finalStatePreview); - - // Show the preview container - noSolutionPreviewContainer.style = 'display:block'; - - return {}; +function visualizeNoSolutions(plot, _data) { + visualizeInfiniteSolutions(plot, _data); } export class LinearCD { @@ -571,17 +547,8 @@ export class LinearCD { } }); - switch (data.type) { - case 'unique solution': - return visualizeUniqueSolution(plot, data); - case 'infinite solutions': - vars = [select1.value, select2.value, ...vars.filter(v => v !== select1.value && v !== select2.value)]; - const solution = solve(struct); - createSliders(solution); - return visualizeInfiniteSolutions(plot, solution); - case 'no solution': - return visualizeNoSolutions(plot, solution); - } + const solution = solve(struct); + visByType(solution); } select1.onchange = update; @@ -590,6 +557,8 @@ export class LinearCD { } function createSliders(data) { + if (data.free <= 0) { return; } + frees = []; const freeVarsContainer = document.getElementById(`slidersContainer`); freeVarsContainer.innerHTML = '
Free Variables:
'; @@ -635,33 +604,9 @@ export class LinearCD { // interaction select.onchange = (d) => { - const select1 = document.getElementById(`var1`); - const select2 = document.getElementById(`var2`); const idx = +d.target.id.split('select-free-')[1]; const previous = frees[idx] - let replaced = ''; - if (d.target.value === select1.value) { - select1.value = previous; - replaced = 'var1' - } - - if (d.target.value === select2.value) { - select2.value = previous; - replaced = 'var2' - } - - if (replaced !== '') { - const other = replaced === "var1" ? `#var2 option` : `#var1 option`; - document.querySelectorAll(other).forEach(opt => { - opt.disabled = false; - if (opt.value === document.getElementById(replaced).value) { - opt.disabled = true; - } - }); - - } - for (let i = 0; i < frees.length; i++) { if (frees[i] === d.target.value && i !== idx) { frees[i] = previous; @@ -669,18 +614,18 @@ export class LinearCD { } frees[idx] = d.target.value; - const deps = vars.filter(v => v !== select1.value && v !== select2.value && !frees.includes(v)); - vars = [select1.value, select2.value, ...deps, ...frees]; + const comp = vars.filter(v => !frees.includes(v)); + vars = [...comp, ...frees]; const solution = solve(struct); createSliders(solution); - return visualizeInfiniteSolutions(plot, solution); + visByType(solution); } slider.oninput = () => { sliders[varName] = parseFloat(slider.value); label.textContent = `${varName}: ${slider.value}`; document.getElementById(`sol-${varName}`).innerHTML = `${varName} = ${slider.value}`; - visualizeInfiniteSolutions(plot, data); + visByType(data); }; sliders[varName] = parseFloat(slider.value); @@ -711,15 +656,7 @@ export class LinearCD { `; generateVariableSelectors(data); - - switch (data.type) { - case 'infinite solutions': - createSliders(data); - break; - case 'no solution': - console.log(data.free); - break; - } + createSliders(data); } draw(data, params, where) { diff --git a/frontend/public/style/colors.css b/frontend/public/style/colors.css index e52701f..fcd71de 100644 --- a/frontend/public/style/colors.css +++ b/frontend/public/style/colors.css @@ -26,9 +26,12 @@ --color-teal-dark: #36afa3; --color-teal-darker: #3ca096; --color-gray-light: #eceff1; + --color-gray-lighter: #fafafa; --color-gray: #b0bec5; --color-gray-dark: #9e9e9e; --color-gray-darker:#757575; --color-lime-light: #f0f4c3; --color-lime: #cddc39; + + } \ No newline at end of file diff --git a/frontend/public/style/inference.css b/frontend/public/style/inference.css index a0b3dbe..27a1b4d 100644 --- a/frontend/public/style/inference.css +++ b/frontend/public/style/inference.css @@ -1,4 +1,4 @@ -@import "./nodes.css"; +/* uses colors from @import "./nodes.css"; */ #inference-view { width: 100%; diff --git a/frontend/public/style/ontology.css b/frontend/public/style/ontology.css index 24466b4..82280e4 100644 --- a/frontend/public/style/ontology.css +++ b/frontend/public/style/ontology.css @@ -1,4 +1,4 @@ -@import "./nodes.css"; +/* uses colors from @import "./nodes.css"; */ :root { --color-node-fill: hsl(205, 87%, 94%); @@ -110,14 +110,14 @@ input:checked + .slider:before { border: 2px solid transparent; border-radius: 4px; - background-color: #fafafa; + background-color: var(--color-gray-lighter); text-align: left; cursor: pointer; font-size: 12px; font-weight: lighter; } .repair-entry:focus { - background-color: #fafafa; + background-color: var(--color-gray-lighter);; border: 2px solid #efefef; } .repair-entry.locked > .lock-sign { diff --git a/frontend/public/style/proof.css b/frontend/public/style/proof.css index 0262f4b..f8dd74e 100644 --- a/frontend/public/style/proof.css +++ b/frontend/public/style/proof.css @@ -1,4 +1,4 @@ -@import "./nodes.css"; +/* uses colors from @import "./nodes.css"; */ :root { --color-conn-rule: var(--color-gray); diff --git a/frontend/public/style/widgets/explanation.css b/frontend/public/style/widgets/explanation.css index 255ad0c..b89bca3 100644 --- a/frontend/public/style/widgets/explanation.css +++ b/frontend/public/style/widgets/explanation.css @@ -1,17 +1,17 @@ -@import "../colors.css"; +/* uses colors from @import "../colors.css"; */ .explanation-popover { position: absolute; display: inline-block; height: fit-content; - background-color: #fafafa; + background-color: var(--color-gray-lighter); box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.12), 0 1px 5px 0 rgba(0, 0, 0, 0.2); } .explanation-container { display: block; width: 100%; - background-color: var(--color-gray-light); + background-color: var(--color-gray-lighter); text-align: justify !important; } @@ -43,7 +43,7 @@ } .explanation-popover>.popoverText { - background-color: #fafafa; + background-color: var(--color-gray-lighter); color: black; text-align: center; border-radius: 2px; diff --git a/frontend/public/style/widgets/general.css b/frontend/public/style/widgets/general.css index d5a6af6..b0c37e2 100644 --- a/frontend/public/style/widgets/general.css +++ b/frontend/public/style/widgets/general.css @@ -1,4 +1,6 @@ +/* single point of entry to load styles - do not duplicate @imports in other .css files! */ @import "../colors.css"; +@import "../nodes.css"; @import "explanation.css"; @import "minimap.css"; @import "sidebar.css"; diff --git a/frontend/views/defs.spy b/frontend/views/defs.spy deleted file mode 100644 index 24efbd6..0000000 --- a/frontend/views/defs.spy +++ /dev/null @@ -1,12 +0,0 @@ - - - - \ No newline at end of file diff --git a/frontend/views/euler/euler.spy b/frontend/views/euler/euler.spy index 97d44b9..c816e01 100644 --- a/frontend/views/euler/euler.spy +++ b/frontend/views/euler/euler.spy @@ -12,12 +12,12 @@ - << menus/menu >> + << widgets/menus/menu >>
- << menus/thumbnail >> + << widgets/menus/thumbnail >>
<< scripts >> diff --git a/frontend/views/inference/inference.spy b/frontend/views/inference/inference.spy index e6f802c..4068b86 100644 --- a/frontend/views/inference/inference.spy +++ b/frontend/views/inference/inference.spy @@ -12,7 +12,7 @@ - << menus/menu >> + << widgets/menus/menu >>
@@ -25,7 +25,7 @@
- << menus/thumbnail >> + << widgets/menus/thumbnail >>
<< scripts >> diff --git a/frontend/views/ontology/ontology.spy b/frontend/views/ontology/ontology.spy deleted file mode 100644 index 5870fdb..0000000 --- a/frontend/views/ontology/ontology.spy +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - {{ title }} - Ontology - - << styles >> - - - - - - - << menus/menu >> - << modals/file-upload >> - << modals/axiom >> - -
- -
- - << menus/settings >> - - << scripts >> - - - - - - diff --git a/frontend/views/main/demo.spy b/frontend/views/pages/demo.spy similarity index 96% rename from frontend/views/main/demo.spy rename to frontend/views/pages/demo.spy index a3f1381..6972c63 100644 --- a/frontend/views/main/demo.spy +++ b/frontend/views/pages/demo.spy @@ -9,7 +9,7 @@ - << menus/menu >> + << widgets/menus/menu >>
@@ -36,7 +36,7 @@
{{ examples }} - << main/progress >> + << widgets/progress >>
diff --git a/frontend/views/main/split.spy b/frontend/views/pages/main.spy similarity index 78% rename from frontend/views/main/split.spy rename to frontend/views/pages/main.spy index a777878..4d19688 100644 --- a/frontend/views/main/split.spy +++ b/frontend/views/pages/main.spy @@ -14,16 +14,16 @@ - << menus/menu >> - << modals/file-upload >> - << modals/advanced-settings >> - << modals/axiom >> + << widgets/menus/menu >> + << widgets/modals/file-upload >> + << widgets/modals/advanced-settings >> + << widgets/modals/axiom >>
- << menus/settings >> + << widgets/menus/settings >> << scripts >> diff --git a/frontend/views/main/test.spy b/frontend/views/pages/test.spy similarity index 100% rename from frontend/views/main/test.spy rename to frontend/views/pages/test.spy diff --git a/frontend/views/main/main.spy b/frontend/views/pages/welcome.spy similarity index 96% rename from frontend/views/main/main.spy rename to frontend/views/pages/welcome.spy index 43c2353..756f841 100644 --- a/frontend/views/main/main.spy +++ b/frontend/views/pages/welcome.spy @@ -10,8 +10,8 @@ - << menus/menu >> - << modals/file-upload >> + << widgets/menus/menu >> + << widgets/modals/file-upload >>
@@ -72,7 +72,7 @@
- << main/progress >> + << widgets/progress >>
diff --git a/frontend/views/proof/proof.spy b/frontend/views/proof/proof.spy deleted file mode 100644 index 96bcbf5..0000000 --- a/frontend/views/proof/proof.spy +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - {{ title }} - Proof - - << styles >> - - - - - - - << menus/menu >> - << modals/file-upload >> - << modals/advanced-settings >> - << modals/axiom >> - -
- -
- - << menus/settings >> - - << scripts >> - - - - - diff --git a/frontend/views/scripts.spy b/frontend/views/scripts.spy index a311aa2..1d5b418 100644 --- a/frontend/views/scripts.spy +++ b/frontend/views/scripts.spy @@ -1,29 +1,24 @@ - + - - - - - + - - + - - + - \ No newline at end of file + + diff --git a/frontend/views/styles.spy b/frontend/views/styles.spy index dd8e050..c9a6763 100644 --- a/frontend/views/styles.spy +++ b/frontend/views/styles.spy @@ -1,10 +1,6 @@ - - - - diff --git a/frontend/views/main/examples-eurovis.spy b/frontend/views/widgets/examples/examples-eurovis.spy similarity index 100% rename from frontend/views/main/examples-eurovis.spy rename to frontend/views/widgets/examples/examples-eurovis.spy diff --git a/frontend/views/main/examples-ijcar.spy b/frontend/views/widgets/examples/examples-ijcar.spy similarity index 100% rename from frontend/views/main/examples-ijcar.spy rename to frontend/views/widgets/examples/examples-ijcar.spy diff --git a/frontend/views/menus/compute.spy b/frontend/views/widgets/menus/compute.spy similarity index 100% rename from frontend/views/menus/compute.spy rename to frontend/views/widgets/menus/compute.spy diff --git a/frontend/views/menus/menu.spy b/frontend/views/widgets/menus/menu.spy similarity index 100% rename from frontend/views/menus/menu.spy rename to frontend/views/widgets/menus/menu.spy diff --git a/frontend/views/menus/projects.spy b/frontend/views/widgets/menus/projects.spy similarity index 100% rename from frontend/views/menus/projects.spy rename to frontend/views/widgets/menus/projects.spy diff --git a/frontend/views/menus/settings.spy b/frontend/views/widgets/menus/settings.spy similarity index 100% rename from frontend/views/menus/settings.spy rename to frontend/views/widgets/menus/settings.spy diff --git a/frontend/views/menus/shortening.spy b/frontend/views/widgets/menus/shortening.spy similarity index 100% rename from frontend/views/menus/shortening.spy rename to frontend/views/widgets/menus/shortening.spy diff --git a/frontend/views/modals/advanced-settings.spy b/frontend/views/widgets/modals/advanced-settings.spy similarity index 100% rename from frontend/views/modals/advanced-settings.spy rename to frontend/views/widgets/modals/advanced-settings.spy diff --git a/frontend/views/modals/axiom.spy b/frontend/views/widgets/modals/axiom.spy similarity index 99% rename from frontend/views/modals/axiom.spy rename to frontend/views/widgets/modals/axiom.spy index 8538784..11cc191 100644 --- a/frontend/views/modals/axiom.spy +++ b/frontend/views/widgets/modals/axiom.spy @@ -76,7 +76,7 @@ - << main/progress >> + << widgets/progress >> diff --git a/frontend/views/modals/file-upload.spy b/frontend/views/widgets/modals/file-upload.spy similarity index 100% rename from frontend/views/modals/file-upload.spy rename to frontend/views/widgets/modals/file-upload.spy diff --git a/frontend/views/main/progress.spy b/frontend/views/widgets/progress.spy similarity index 100% rename from frontend/views/main/progress.spy rename to frontend/views/widgets/progress.spy diff --git a/server.js b/server.js index 26ba9d1..3304088 100644 --- a/server.js +++ b/server.js @@ -51,7 +51,7 @@ if (!existsSync(dataDir)) { } app.get('/test', (req, res) => { - res.render('main/test.spy', { + res.render('pages/test.spy', { title: "evonne lib", uuid: uuidv4(), }); @@ -62,57 +62,27 @@ app.get('/uuid', (req, res) => { }); // pages -app.get('/', (req, res) => { +const page = (req, res) => { const id = req.query.id; if (!id) { renderMain(req, res); } else { ensureIfExample(id, res); - res.render('main/split.spy', { + res.render('pages/main.spy', { title, uuid: id, settings_specific: '<< proof/settings >> << ontology/settings >> << ce/settings >>', advanced_settings_specific: '<< proof/advanced-settings >>', sidebars_specific: '<< ontology/repairs >>', - menu_specific: `${MODE === 'demo' ? '' : '<< menus/projects >> << menus/compute >>'} << proof/menu >> << ontology/menu >>`, - general_settings: '<< menus/shortening >>' + menu_specific: `${MODE === 'demo' ? '' : '<< widgets/menus/projects >> << widgets/menus/compute >>'} << proof/menu >> << ontology/menu >>`, + general_settings: '<< widgets/menus/shortening >>' }); } -}); - -app.get('/ontology', (req, res) => { - const id = req.query.id; - if (!id) { - renderMain(req, res); - } else { - ensureIfExample(id, res); - res.render('ontology/ontology.spy', { - title, - uuid: id, - settings_specific: '<< ontology/settings >>', - sidebars_specific: '<< ontology/repairs >>', - menu_specific: `${MODE === 'demo' ? '' : '<< menus/projects >> << menus/compute >>'} << ontology/menu >>`, - general_settings: '<< menus/shortening >>' - }); - } -}); +} -app.get('/proof', (req, res) => { - const id = req.query.id; - if (!id) { - renderMain(req, res); - } else { - ensureIfExample(id, res); - res.render('proof/proof.spy', { - title, - uuid: id, - settings_specific: '<< proof/settings >>', - advanced_settings_specific: '<< proof/advanced-settings >>', - menu_specific: `${MODE === 'demo' ? '' : '<< menus/projects >> << menus/compute >>'} << proof/menu >>`, - general_settings: '<< menus/shortening >>' - }); - } -}); +app.get('/', page); +app.get('/ontology', page); +app.get('/proof', page); app.get('/euler', (_, response) => { response.render('euler/euler.spy') @@ -529,21 +499,21 @@ function printOutput(p) { } function getExamples(req) { - return `<< main/examples-${req.query.examples ? req.query.examples : EXAMPLES} >>`; + return `<< widgets/examples/examples-${req.query.examples ? req.query.examples : EXAMPLES} >>`; } function renderMain(req, res) { if (MODE === 'demo') { - res.render('main/demo.spy', { + res.render('pages/demo.spy', { title, uuid: uuidv4(), examples: getExamples(req), }); } else { - res.render('main/main.spy', { + res.render('pages/welcome.spy', { title, uuid: uuidv4(), - menu_specific: '<< menus/projects >>', + menu_specific: '<< widgets/menus/projects >>', examples: getExamples(req), }); }