Refine admin dock and access card editing
This commit is contained in:
parent
8472611ef3
commit
25e5afedf2
|
|
@ -919,6 +919,54 @@ body[data-file-picker-active="true"] .editor {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.boolean-field {
|
||||||
|
align-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boolean-field input,
|
||||||
|
.group-head-toggle input {
|
||||||
|
width: 2.42rem;
|
||||||
|
height: 1.32rem;
|
||||||
|
margin: 0;
|
||||||
|
appearance: none;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(255, 255, 255, 0.16);
|
||||||
|
padding: 0.16rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boolean-field input::before,
|
||||||
|
.group-head-toggle input::before {
|
||||||
|
display: block;
|
||||||
|
width: 1rem;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, 0.82);
|
||||||
|
content: "";
|
||||||
|
transition: transform 0.16s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boolean-field input:checked,
|
||||||
|
.group-head-toggle input:checked {
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.boolean-field input:checked::before,
|
||||||
|
.group-head-toggle input:checked::before {
|
||||||
|
background: #050506;
|
||||||
|
transform: translateX(1.1rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-head-toggle {
|
||||||
|
display: inline-flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.52rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.78rem;
|
||||||
|
font-weight: 850;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.collection-field {
|
.collection-field {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
|
|
|
||||||
208
admin/admin.js
208
admin/admin.js
|
|
@ -80,8 +80,32 @@ const STATIC_FALLBACK = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
dock: {
|
dock: {
|
||||||
appTarget: "app-data",
|
items: [
|
||||||
trashAlt: "Белая иконка корзины.",
|
{
|
||||||
|
enabled: true,
|
||||||
|
title: "NODE.DC",
|
||||||
|
href: "#hero",
|
||||||
|
linkTarget: "",
|
||||||
|
targetId: "app-data",
|
||||||
|
iconSrc: "./assets/webflow/images/69ad964d951f9d17cc5a919e_logo-app.svg",
|
||||||
|
iconAlt: "Иконка NODE.DC",
|
||||||
|
className: "dock-icon ssscript",
|
||||||
|
iconClass: "logo",
|
||||||
|
dividerBefore: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: true,
|
||||||
|
title: "Корзина",
|
||||||
|
href: "",
|
||||||
|
linkTarget: "",
|
||||||
|
targetId: "",
|
||||||
|
iconSrc: "./assets/webflow/images/69a9ae818a76c773dc3cf7bc_trash.avif",
|
||||||
|
iconAlt: "Белая иконка корзины.",
|
||||||
|
className: "dock-icon no-bg",
|
||||||
|
iconClass: "dock-app-img trash",
|
||||||
|
dividerBefore: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
demoFileLabel: "демо-видео.txt",
|
demoFileLabel: "демо-видео.txt",
|
||||||
},
|
},
|
||||||
assets: {
|
assets: {
|
||||||
|
|
@ -126,8 +150,33 @@ const STATIC_FALLBACK = {
|
||||||
field("body", "Текст", "textarea", "text", "Уведомления"),
|
field("body", "Текст", "textarea", "text", "Уведомления"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
field("content.dock.appTarget", "Dock: ID приложения", "text", "text", "Dock"),
|
collection(
|
||||||
field("content.dock.trashAlt", "Dock: alt корзины", "text", "text", "Dock"),
|
"content.dock.items",
|
||||||
|
"Иконки dock-панели",
|
||||||
|
"Dock",
|
||||||
|
{
|
||||||
|
enabled: true,
|
||||||
|
title: "Новое приложение",
|
||||||
|
href: "#hero",
|
||||||
|
linkTarget: "",
|
||||||
|
targetId: "",
|
||||||
|
iconSrc: "./assets/webflow/images/69ad964d951f9d17cc5a919e_logo-app.svg",
|
||||||
|
iconAlt: "Иконка приложения",
|
||||||
|
className: "dock-icon ssscript",
|
||||||
|
iconClass: "logo",
|
||||||
|
dividerBefore: false,
|
||||||
|
},
|
||||||
|
[
|
||||||
|
field("enabled", "Отображение", "boolean", "boolean", "Dock"),
|
||||||
|
field("title", "Название", "text", "text", "Dock"),
|
||||||
|
field("href", "Ссылка / адрес", "url", "attr", "Dock"),
|
||||||
|
field("linkTarget", "Target ссылки", "text", "attr", "Dock"),
|
||||||
|
field("targetId", "ID окна / приложения", "text", "attr", "Dock"),
|
||||||
|
field("iconSrc", "Иконка", "media", "attr", "Dock"),
|
||||||
|
field("iconAlt", "Alt иконки", "text", "text", "Dock"),
|
||||||
|
field("dividerBefore", "Разделитель слева", "boolean", "boolean", "Dock"),
|
||||||
|
],
|
||||||
|
),
|
||||||
field("content.dock.demoFileLabel", "Dock: подпись демо-файла", "text", "text", "Dock"),
|
field("content.dock.demoFileLabel", "Dock: подпись демо-файла", "text", "text", "Dock"),
|
||||||
field("content.assets.webglModelSrc", "WebGL-модель логотипа", "media", "attr", "Модель и логотипы"),
|
field("content.assets.webglModelSrc", "WebGL-модель логотипа", "media", "attr", "Модель и логотипы"),
|
||||||
field("content.assets.logoPng", "PNG-логотип", "media", "attr", "Модель и логотипы"),
|
field("content.assets.logoPng", "PNG-логотип", "media", "attr", "Модель и логотипы"),
|
||||||
|
|
@ -143,11 +192,11 @@ const GROUP_COPY = {
|
||||||
"Шапка: логотип": "Статичный знак верхней mac-панели: файл логотипа, alt и брендовая подпись.",
|
"Шапка: логотип": "Статичный знак верхней mac-панели: файл логотипа, alt и брендовая подпись.",
|
||||||
"Шапка: ссылки": "Пункты навигации верхней mac-панели. Каждый пункт можно добавить, удалить и переставить.",
|
"Шапка: ссылки": "Пункты навигации верхней mac-панели. Каждый пункт можно добавить, удалить и переставить.",
|
||||||
Уведомления: "Карточки уведомлений справа сверху. Порядок в админке соответствует порядку на сайте.",
|
Уведомления: "Карточки уведомлений справа сверху. Порядок в админке соответствует порядку на сайте.",
|
||||||
Dock: "Нижняя системная панель: связь кнопки приложения, корзина и подписи файлов.",
|
Dock: "Нижняя системная панель: приложения, корзина, ссылки и подписи файлов.",
|
||||||
"Модель и логотипы": "Базовые ассеты статичного слоя: GLB-модель, PNG/SVG-логотипы и alt-тексты.",
|
"Модель и логотипы": "Базовые ассеты статичного слоя: GLB-модель, PNG/SVG-логотипы и alt-тексты.",
|
||||||
Заголовок: "Главный заголовок и вводный текст выбранной секции.",
|
Заголовок: "Главный заголовок и вводный текст выбранной секции.",
|
||||||
"Тезисы первого экрана": "Четыре смысловых тезиса вокруг формы раннего доступа.",
|
"Тезисы первого экрана": "Четыре смысловых тезиса вокруг формы раннего доступа.",
|
||||||
"Карточка раннего доступа": "Текст внутри центрального окна приложения.",
|
"Карточка доступа": "Логотипы, тексты и видимость центрального окна приложения.",
|
||||||
Форма: "Плейсхолдеры, кнопки, success/failure состояния формы.",
|
Форма: "Плейсхолдеры, кнопки, success/failure состояния формы.",
|
||||||
"Юридический блок": "Политики, права и нижний текст формы.",
|
"Юридический блок": "Политики, права и нижний текст формы.",
|
||||||
"Видео-окно": "Окно демо-видео и подключенный медиа-файл.",
|
"Видео-окно": "Окно демо-видео и подключенный медиа-файл.",
|
||||||
|
|
@ -270,7 +319,7 @@ function inferFieldGroup(path) {
|
||||||
if (path.startsWith("content.dock.")) return "Dock";
|
if (path.startsWith("content.dock.")) return "Dock";
|
||||||
if (path.startsWith("content.assets.")) return "Модель и логотипы";
|
if (path.startsWith("content.assets.")) return "Модель и логотипы";
|
||||||
if (path.startsWith("content.cards.")) return "Тезисы первого экрана";
|
if (path.startsWith("content.cards.")) return "Тезисы первого экрана";
|
||||||
if (path.startsWith("content.app.")) return "Карточка раннего доступа";
|
if (path.startsWith("content.app.") || path.startsWith("content.accessCard.")) return "Карточка доступа";
|
||||||
if (path.startsWith("content.form.")) return "Форма";
|
if (path.startsWith("content.form.")) return "Форма";
|
||||||
if (path === "content.legalHtml") return "Юридический блок";
|
if (path === "content.legalHtml") return "Юридический блок";
|
||||||
if (path === "content.windowTitle" || path === "content.videoSrc") return "Видео-окно";
|
if (path === "content.windowTitle" || path === "content.videoSrc") return "Видео-окно";
|
||||||
|
|
@ -289,6 +338,63 @@ function inferFieldGroup(path) {
|
||||||
return "Контент";
|
return "Контент";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createDefaultDockItems(dock = {}, assets = {}) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
enabled: true,
|
||||||
|
title: "NODE.DC",
|
||||||
|
href: "#hero",
|
||||||
|
linkTarget: "",
|
||||||
|
targetId: dock.appTarget || "app-data",
|
||||||
|
iconSrc: assets.appIconSrc || STATIC_FALLBACK.content.assets.appIconSrc,
|
||||||
|
iconAlt: assets.appIconAlt || STATIC_FALLBACK.content.assets.appIconAlt,
|
||||||
|
className: "dock-icon ssscript",
|
||||||
|
iconClass: "logo",
|
||||||
|
dividerBefore: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: true,
|
||||||
|
title: "Корзина",
|
||||||
|
href: "",
|
||||||
|
linkTarget: "",
|
||||||
|
targetId: "",
|
||||||
|
iconSrc: "./assets/webflow/images/69a9ae818a76c773dc3cf7bc_trash.avif",
|
||||||
|
iconAlt: dock.trashAlt || "Белая иконка корзины.",
|
||||||
|
className: "dock-icon no-bg",
|
||||||
|
iconClass: "dock-app-img trash",
|
||||||
|
dividerBefore: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDock(dock, assets = {}) {
|
||||||
|
const fallbackItems = createDefaultDockItems(dock, assets);
|
||||||
|
|
||||||
|
if (!Array.isArray(dock.items)) {
|
||||||
|
dock.items = fallbackItems;
|
||||||
|
} else {
|
||||||
|
dock.items = dock.items.map((item, index) => {
|
||||||
|
const fallback = fallbackItems[index] || fallbackItems[0];
|
||||||
|
return {
|
||||||
|
enabled: item.enabled !== false,
|
||||||
|
title: item.title || fallback.title || `Элемент ${index + 1}`,
|
||||||
|
href: item.href ?? fallback.href ?? "",
|
||||||
|
linkTarget: item.linkTarget ?? fallback.linkTarget ?? "",
|
||||||
|
targetId: item.targetId ?? fallback.targetId ?? "",
|
||||||
|
iconSrc: item.iconSrc || fallback.iconSrc,
|
||||||
|
iconAlt: item.iconAlt || item.alt || fallback.iconAlt,
|
||||||
|
className: item.className || fallback.className || "dock-icon ssscript",
|
||||||
|
iconClass: item.iconClass || fallback.iconClass || "logo",
|
||||||
|
dividerBefore: Boolean(item.dividerBefore),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dock.demoFileLabel ||= STATIC_FALLBACK.content.dock.demoFileLabel;
|
||||||
|
delete dock.appTarget;
|
||||||
|
delete dock.trashAlt;
|
||||||
|
}
|
||||||
|
|
||||||
function ensureStaticElements() {
|
function ensureStaticElements() {
|
||||||
if (!state.page.staticElements) {
|
if (!state.page.staticElements) {
|
||||||
state.page.staticElements = clone(STATIC_FALLBACK);
|
state.page.staticElements = clone(STATIC_FALLBACK);
|
||||||
|
|
@ -307,8 +413,9 @@ function ensureStaticElements() {
|
||||||
state.page.staticElements.content.notifications.forEach((notification, index) => {
|
state.page.staticElements.content.notifications.forEach((notification, index) => {
|
||||||
notification.eyebrowClass ||= STATIC_FALLBACK.content.notifications[index]?.eyebrowClass || "red-txt gray";
|
notification.eyebrowClass ||= STATIC_FALLBACK.content.notifications[index]?.eyebrowClass || "red-txt gray";
|
||||||
});
|
});
|
||||||
state.page.staticElements.content.dock ||= clone(STATIC_FALLBACK.content.dock);
|
|
||||||
state.page.staticElements.content.assets ||= clone(STATIC_FALLBACK.content.assets);
|
state.page.staticElements.content.assets ||= clone(STATIC_FALLBACK.content.assets);
|
||||||
|
state.page.staticElements.content.dock ||= clone(STATIC_FALLBACK.content.dock);
|
||||||
|
normalizeDock(state.page.staticElements.content.dock, state.page.staticElements.content.assets);
|
||||||
state.page.staticElements.editableFields = clone(STATIC_FALLBACK.editableFields);
|
state.page.staticElements.editableFields = clone(STATIC_FALLBACK.editableFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1286,7 +1393,71 @@ function prepareCollectionItemForInsert(items, item) {
|
||||||
return nextItem;
|
return nextItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function syncFieldValue(block, path, input) {
|
||||||
|
setByPath(block, path, input.type === "checkbox" ? input.checked : input.value);
|
||||||
|
el.json.value = JSON.stringify(block, null, 2);
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBooleanField({ block, editableField, grid }) {
|
||||||
|
const label = document.createElement("label");
|
||||||
|
const labelRow = document.createElement("span");
|
||||||
|
const labelText = document.createElement("span");
|
||||||
|
const kind = document.createElement("span");
|
||||||
|
const path = document.createElement("span");
|
||||||
|
const input = document.createElement("input");
|
||||||
|
|
||||||
|
label.className = "field-control boolean-field";
|
||||||
|
labelRow.className = "field-label-row";
|
||||||
|
labelText.className = "field-label-text";
|
||||||
|
labelText.textContent = editableField.label || editableField.path;
|
||||||
|
kind.className = "field-kind";
|
||||||
|
kind.textContent = editableField.kind || "boolean";
|
||||||
|
path.className = "field-path";
|
||||||
|
path.textContent = editableField.path;
|
||||||
|
labelRow.append(labelText, kind);
|
||||||
|
|
||||||
|
input.dataset.path = editableField.path;
|
||||||
|
input.type = "checkbox";
|
||||||
|
input.checked = Boolean(getByPath(block, editableField.path));
|
||||||
|
|
||||||
|
input.addEventListener("change", () => {
|
||||||
|
const active = activeBlock();
|
||||||
|
if (!active) return;
|
||||||
|
syncFieldValue(active, editableField.path, input);
|
||||||
|
});
|
||||||
|
|
||||||
|
label.append(labelRow, input, path);
|
||||||
|
grid.append(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderHeaderBooleanToggle({ block, editableField, head }) {
|
||||||
|
const label = document.createElement("label");
|
||||||
|
const input = document.createElement("input");
|
||||||
|
const text = document.createElement("span");
|
||||||
|
|
||||||
|
label.className = "group-head-toggle";
|
||||||
|
input.dataset.path = editableField.path;
|
||||||
|
input.type = "checkbox";
|
||||||
|
input.checked = Boolean(getByPath(block, editableField.path));
|
||||||
|
text.textContent = "Отображение";
|
||||||
|
|
||||||
|
input.addEventListener("change", () => {
|
||||||
|
const active = activeBlock();
|
||||||
|
if (!active) return;
|
||||||
|
syncFieldValue(active, editableField.path, input);
|
||||||
|
});
|
||||||
|
|
||||||
|
label.append(input, text);
|
||||||
|
head.append(label);
|
||||||
|
}
|
||||||
|
|
||||||
function renderCollectionScalarField({ block, field, grid }) {
|
function renderCollectionScalarField({ block, field, grid }) {
|
||||||
|
if (field.kind === "boolean") {
|
||||||
|
renderBooleanField({ block, editableField: field, grid });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const label = document.createElement("label");
|
const label = document.createElement("label");
|
||||||
const labelRow = document.createElement("span");
|
const labelRow = document.createElement("span");
|
||||||
const labelText = document.createElement("span");
|
const labelText = document.createElement("span");
|
||||||
|
|
@ -1317,9 +1488,7 @@ function renderCollectionScalarField({ block, field, grid }) {
|
||||||
input.addEventListener("input", () => {
|
input.addEventListener("input", () => {
|
||||||
const active = activeBlock();
|
const active = activeBlock();
|
||||||
if (!active) return;
|
if (!active) return;
|
||||||
setByPath(active, field.path, input.value);
|
syncFieldValue(active, field.path, input);
|
||||||
el.json.value = JSON.stringify(active, null, 2);
|
|
||||||
markDirty();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
label.append(labelRow, input, path);
|
label.append(labelRow, input, path);
|
||||||
|
|
@ -1534,7 +1703,11 @@ function renderContentFields(block) {
|
||||||
head.append(titleWrap);
|
head.append(titleWrap);
|
||||||
grid.className = "group-grid";
|
grid.className = "group-grid";
|
||||||
|
|
||||||
for (const editableField of fields) {
|
const headerFields = fields.filter((field) => field.kind === "boolean" && field.headerToggle);
|
||||||
|
const bodyFields = fields.filter((field) => !headerFields.includes(field));
|
||||||
|
headerFields.forEach((editableField) => renderHeaderBooleanToggle({ block, editableField, head }));
|
||||||
|
|
||||||
|
for (const editableField of bodyFields) {
|
||||||
if (editableField.kind === "collection") {
|
if (editableField.kind === "collection") {
|
||||||
renderCollectionField({ block, editableField, grid });
|
renderCollectionField({ block, editableField, grid });
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1545,6 +1718,11 @@ function renderContentFields(block) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (editableField.kind === "boolean") {
|
||||||
|
renderBooleanField({ block, editableField, grid });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const label = document.createElement("label");
|
const label = document.createElement("label");
|
||||||
const labelRow = document.createElement("span");
|
const labelRow = document.createElement("span");
|
||||||
const labelText = document.createElement("span");
|
const labelText = document.createElement("span");
|
||||||
|
|
@ -1575,9 +1753,7 @@ function renderContentFields(block) {
|
||||||
input.addEventListener("input", () => {
|
input.addEventListener("input", () => {
|
||||||
const active = activeBlock();
|
const active = activeBlock();
|
||||||
if (!active) return;
|
if (!active) return;
|
||||||
setByPath(active, editableField.path, input.value);
|
syncFieldValue(active, editableField.path, input);
|
||||||
el.json.value = JSON.stringify(active, null, 2);
|
|
||||||
markDirty();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
label.append(labelRow, input, path);
|
label.append(labelRow, input, path);
|
||||||
|
|
@ -1594,7 +1770,7 @@ function syncContentFields() {
|
||||||
if (!block) return;
|
if (!block) return;
|
||||||
|
|
||||||
for (const input of el.contentFields.querySelectorAll("[data-path]")) {
|
for (const input of el.contentFields.querySelectorAll("[data-path]")) {
|
||||||
setByPath(block, input.dataset.path, input.value);
|
setByPath(block, input.dataset.path, input.type === "checkbox" ? input.checked : input.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
|
|
@ -35,13 +35,6 @@
|
||||||
"bodyHtml": "<span class=\"darker-70\">Деплой в один клик,</span> непрерывный пайплайн, мгновенный откат и быстрая <span class=\"darker-70\">доставка через CDN.</span>"
|
"bodyHtml": "<span class=\"darker-70\">Деплой в один клик,</span> непрерывный пайплайн, мгновенный откат и быстрая <span class=\"darker-70\">доставка через CDN.</span>"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"app": {
|
|
||||||
"iconAlt": "Иконка NODE.DC",
|
|
||||||
"logoAlt": "Логотип NODE.DC",
|
|
||||||
"release": "Бета-релиз: июль 2026",
|
|
||||||
"heading": "Ранний доступ",
|
|
||||||
"description": "Кастомный код, MCP и ИИ-агенты для Webflow в одном рабочем контуре."
|
|
||||||
},
|
|
||||||
"form": {
|
"form": {
|
||||||
"messagePlaceholder": "Сообщение (необязательно)",
|
"messagePlaceholder": "Сообщение (необязательно)",
|
||||||
"namePlaceholder": "Ваше имя*",
|
"namePlaceholder": "Ваше имя*",
|
||||||
|
|
@ -56,7 +49,17 @@
|
||||||
"failureHeading": "Не получилось",
|
"failureHeading": "Не получилось",
|
||||||
"failureTextHtml": "Что-то пошло не так. <br/>Попробуйте ещё раз чуть позже.<br/>"
|
"failureTextHtml": "Что-то пошло не так. <br/>Попробуйте ещё раз чуть позже.<br/>"
|
||||||
},
|
},
|
||||||
"legalHtml": "<a href=\"https://docs.google.com/document/d/1vME2w8ZB88HoH-JyvkkiL7exrcMfWhkERfFRlnIeZFA/edit?usp=sharing\" target=\"_blank\">Политика конфиденциальности</a> • <a href=\"https://docs.google.com/document/d/1vME2w8ZB88HoH-JyvkkiL7exrcMfWhkERfFRlnIeZFA/edit?usp=sharing\" target=\"_blank\">Политика cookie</a><br/>© — Все права защищены<br/>2026"
|
"legalHtml": "<a href=\"https://docs.google.com/document/d/1vME2w8ZB88HoH-JyvkkiL7exrcMfWhkERfFRlnIeZFA/edit?usp=sharing\" target=\"_blank\">Политика конфиденциальности</a> • <a href=\"https://docs.google.com/document/d/1vME2w8ZB88HoH-JyvkkiL7exrcMfWhkERfFRlnIeZFA/edit?usp=sharing\" target=\"_blank\">Политика cookie</a><br/>© — Все права защищены<br/>2026",
|
||||||
|
"accessCard": {
|
||||||
|
"enabled": true,
|
||||||
|
"iconSrc": "./assets/webflow/images/69ad964d951f9d17cc5a919e_logo-app.svg",
|
||||||
|
"iconAlt": "Иконка NODE.DC",
|
||||||
|
"wordmarkSrc": "./assets/webflow/images/69ad96a28b06b7552a339137_logtype.svg?v=node-dc",
|
||||||
|
"wordmarkAlt": "Логотип NODE.DC",
|
||||||
|
"release": "Бета-релиз: июль 2026",
|
||||||
|
"heading": "Ранний доступ",
|
||||||
|
"description": "Кастомный код, MCP и ИИ-агенты для Webflow в одном рабочем контуре."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"editableFields": [
|
"editableFields": [
|
||||||
{
|
{
|
||||||
|
|
@ -144,39 +147,61 @@
|
||||||
"group": "Тезисы первого экрана"
|
"group": "Тезисы первого экрана"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.iconAlt",
|
"path": "content.accessCard.enabled",
|
||||||
"label": "App: alt иконки",
|
"label": "Карточка доступа: отображение",
|
||||||
"kind": "text",
|
"kind": "boolean",
|
||||||
|
"renderAs": "boolean",
|
||||||
|
"group": "Карточка доступа",
|
||||||
|
"headerToggle": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "content.accessCard.iconSrc",
|
||||||
|
"label": "Карточка доступа: иконка",
|
||||||
|
"kind": "media",
|
||||||
"renderAs": "attr",
|
"renderAs": "attr",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.logoAlt",
|
"path": "content.accessCard.wordmarkSrc",
|
||||||
"label": "App: alt логотипа",
|
"label": "Карточка доступа: wordmark",
|
||||||
"kind": "text",
|
"kind": "media",
|
||||||
"renderAs": "attr",
|
"renderAs": "attr",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.release",
|
"path": "content.accessCard.iconAlt",
|
||||||
"label": "App: релиз",
|
"label": "Карточка доступа: alt иконки",
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.heading",
|
"path": "content.accessCard.wordmarkAlt",
|
||||||
"label": "App: заголовок",
|
"label": "Карточка доступа: alt логотипа",
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.description",
|
"path": "content.accessCard.release",
|
||||||
"label": "App: описание",
|
"label": "Карточка доступа: релиз",
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "content.accessCard.heading",
|
||||||
|
"label": "Карточка доступа: заголовок",
|
||||||
|
"kind": "text",
|
||||||
|
"renderAs": "text",
|
||||||
|
"group": "Карточка доступа"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "content.accessCard.description",
|
||||||
|
"label": "Карточка доступа: описание",
|
||||||
|
"kind": "textarea",
|
||||||
|
"renderAs": "text",
|
||||||
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.form.messagePlaceholder",
|
"path": "content.form.messagePlaceholder",
|
||||||
|
|
@ -2042,15 +2067,6 @@
|
||||||
"anchor": "waitlist",
|
"anchor": "waitlist",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"content": {
|
"content": {
|
||||||
"app": {
|
|
||||||
"iconSrc": "./assets/webflow/images/69ad964d951f9d17cc5a919e_logo-app.svg",
|
|
||||||
"iconAlt": "Иконка NODE.DC",
|
|
||||||
"wordmarkSrc": "./assets/webflow/images/69ad96a28b06b7552a339137_logtype.svg?v=node-dc",
|
|
||||||
"wordmarkAlt": "Логотип NODE.DC",
|
|
||||||
"release": "Бета-релиз: июль 2026",
|
|
||||||
"heading": "Ранний доступ",
|
|
||||||
"description": "Кастомный код, MCP и ИИ-агенты для Webflow в одном рабочем контуре."
|
|
||||||
},
|
|
||||||
"form": {
|
"form": {
|
||||||
"messagePlaceholder": "Сообщение (необязательно)",
|
"messagePlaceholder": "Сообщение (необязательно)",
|
||||||
"namePlaceholder": "Ваше имя*",
|
"namePlaceholder": "Ваше имя*",
|
||||||
|
|
@ -2303,57 +2319,75 @@
|
||||||
"iconSrcset": "./assets/webflow/images/69a9fb87bc56142371745926_txt-file-p-500.avif 500w, ./assets/webflow/images/69a9fb87bc56142371745926_txt-file.avif 1024w",
|
"iconSrcset": "./assets/webflow/images/69a9fb87bc56142371745926_txt-file-p-500.avif 500w, ./assets/webflow/images/69a9fb87bc56142371745926_txt-file.avif 1024w",
|
||||||
"iconAlt": "Иконка TXT-файла"
|
"iconAlt": "Иконка TXT-файла"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"accessCard": {
|
||||||
|
"enabled": true,
|
||||||
|
"iconSrc": "./assets/webflow/images/69ad964d951f9d17cc5a919e_logo-app.svg",
|
||||||
|
"iconAlt": "Иконка NODE.DC",
|
||||||
|
"wordmarkSrc": "./assets/webflow/images/69ad96a28b06b7552a339137_logtype.svg?v=node-dc",
|
||||||
|
"wordmarkAlt": "Логотип NODE.DC",
|
||||||
|
"release": "Бета-релиз: июль 2026",
|
||||||
|
"heading": "Ранний доступ",
|
||||||
|
"description": "Кастомный код, MCP и ИИ-агенты для Webflow в одном рабочем контуре."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"editableFields": [
|
"editableFields": [
|
||||||
{
|
{
|
||||||
"path": "content.app.iconSrc",
|
"path": "content.accessCard.enabled",
|
||||||
"label": "Иконка приложения",
|
"label": "Карточка доступа: отображение",
|
||||||
|
"kind": "boolean",
|
||||||
|
"renderAs": "boolean",
|
||||||
|
"group": "Карточка доступа",
|
||||||
|
"headerToggle": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "content.accessCard.iconSrc",
|
||||||
|
"label": "Карточка доступа: иконка",
|
||||||
"kind": "media",
|
"kind": "media",
|
||||||
"renderAs": "attr",
|
"renderAs": "attr",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.iconAlt",
|
"path": "content.accessCard.wordmarkSrc",
|
||||||
"label": "Иконка: alt",
|
"label": "Карточка доступа: wordmark",
|
||||||
"kind": "text",
|
|
||||||
"renderAs": "attr",
|
|
||||||
"group": "Карточка раннего доступа"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "content.app.wordmarkSrc",
|
|
||||||
"label": "Wordmark",
|
|
||||||
"kind": "media",
|
"kind": "media",
|
||||||
"renderAs": "attr",
|
"renderAs": "attr",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.wordmarkAlt",
|
"path": "content.accessCard.iconAlt",
|
||||||
"label": "Wordmark: alt",
|
"label": "Карточка доступа: alt иконки",
|
||||||
"kind": "text",
|
|
||||||
"renderAs": "attr",
|
|
||||||
"group": "Карточка раннего доступа"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "content.app.release",
|
|
||||||
"label": "Релиз",
|
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.heading",
|
"path": "content.accessCard.wordmarkAlt",
|
||||||
"label": "Заголовок",
|
"label": "Карточка доступа: alt логотипа",
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.description",
|
"path": "content.accessCard.release",
|
||||||
"label": "Описание",
|
"label": "Карточка доступа: релиз",
|
||||||
|
"kind": "text",
|
||||||
|
"renderAs": "text",
|
||||||
|
"group": "Карточка доступа"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "content.accessCard.heading",
|
||||||
|
"label": "Карточка доступа: заголовок",
|
||||||
|
"kind": "text",
|
||||||
|
"renderAs": "text",
|
||||||
|
"group": "Карточка доступа"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "content.accessCard.description",
|
||||||
|
"label": "Карточка доступа: описание",
|
||||||
"kind": "textarea",
|
"kind": "textarea",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.form.messagePlaceholder",
|
"path": "content.form.messagePlaceholder",
|
||||||
|
|
|
||||||
|
|
@ -57,15 +57,39 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dock": {
|
"dock": {
|
||||||
"appTarget": "app-data",
|
"demoFileLabel": "демо-видео.txt",
|
||||||
"trashAlt": "Белая иконка корзины.",
|
"items": [
|
||||||
"demoFileLabel": "демо-видео.txt"
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"title": "NODE.DC",
|
||||||
|
"href": "#hero",
|
||||||
|
"linkTarget": "",
|
||||||
|
"targetId": "app-data",
|
||||||
|
"iconSrc": "./assets/uploads/images/static-elements/dclogo_white_alpha-mqus5if2.png",
|
||||||
|
"iconAlt": "Иконка NODE.DC",
|
||||||
|
"className": "dock-icon ssscript",
|
||||||
|
"iconClass": "logo",
|
||||||
|
"dividerBefore": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enabled": true,
|
||||||
|
"title": "Корзина",
|
||||||
|
"href": "",
|
||||||
|
"linkTarget": "",
|
||||||
|
"targetId": "",
|
||||||
|
"iconSrc": "./assets/webflow/images/69a9ae818a76c773dc3cf7bc_trash.avif",
|
||||||
|
"iconAlt": "Белая иконка корзины.",
|
||||||
|
"className": "dock-icon no-bg",
|
||||||
|
"iconClass": "dock-app-img trash",
|
||||||
|
"dividerBefore": true
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"assets": {
|
"assets": {
|
||||||
"webglModelSrc": "./assets/custom/icon.glb",
|
"webglModelSrc": "./assets/custom/icon.glb",
|
||||||
"logoPng": "./assets/custom/logo.png",
|
"logoPng": "./assets/custom/logo.png",
|
||||||
"logoPngAlt": "Логотип NODE.DC",
|
"logoPngAlt": "Логотип NODE.DC",
|
||||||
"appIconSrc": "./assets/webflow/images/69ad964d951f9d17cc5a919e_logo-app.svg",
|
"appIconSrc": "./assets/uploads/images/static-elements/dclogo_white_alpha-mqus5if2.png",
|
||||||
"appIconAlt": "Иконка NODE.DC",
|
"appIconAlt": "Иконка NODE.DC",
|
||||||
"appWordmarkSrc": "./assets/webflow/images/69ad96a28b06b7552a339137_logtype.svg?v=node-dc",
|
"appWordmarkSrc": "./assets/webflow/images/69ad96a28b06b7552a339137_logtype.svg?v=node-dc",
|
||||||
"appWordmarkAlt": "Логотип NODE.DC"
|
"appWordmarkAlt": "Логотип NODE.DC"
|
||||||
|
|
@ -172,19 +196,82 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.dock.appTarget",
|
"path": "content.dock.items",
|
||||||
"label": "Dock: ID приложения",
|
"label": "Иконки dock-панели",
|
||||||
|
"kind": "collection",
|
||||||
|
"renderAs": "collection",
|
||||||
|
"group": "Dock",
|
||||||
|
"itemTemplate": {
|
||||||
|
"enabled": true,
|
||||||
|
"title": "Новое приложение",
|
||||||
|
"href": "#hero",
|
||||||
|
"linkTarget": "",
|
||||||
|
"targetId": "",
|
||||||
|
"iconSrc": "./assets/webflow/images/69ad964d951f9d17cc5a919e_logo-app.svg",
|
||||||
|
"iconAlt": "Иконка приложения",
|
||||||
|
"className": "dock-icon ssscript",
|
||||||
|
"iconClass": "logo",
|
||||||
|
"dividerBefore": false
|
||||||
|
},
|
||||||
|
"itemFields": [
|
||||||
|
{
|
||||||
|
"path": "enabled",
|
||||||
|
"label": "Отображение",
|
||||||
|
"kind": "boolean",
|
||||||
|
"renderAs": "boolean",
|
||||||
|
"group": "Dock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "title",
|
||||||
|
"label": "Название",
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Dock"
|
"group": "Dock"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.dock.trashAlt",
|
"path": "href",
|
||||||
"label": "Dock: alt корзины",
|
"label": "Ссылка / адрес",
|
||||||
|
"kind": "url",
|
||||||
|
"renderAs": "attr",
|
||||||
|
"group": "Dock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "linkTarget",
|
||||||
|
"label": "Target ссылки",
|
||||||
|
"kind": "text",
|
||||||
|
"renderAs": "attr",
|
||||||
|
"group": "Dock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "targetId",
|
||||||
|
"label": "ID окна / приложения",
|
||||||
|
"kind": "text",
|
||||||
|
"renderAs": "attr",
|
||||||
|
"group": "Dock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "iconSrc",
|
||||||
|
"label": "Иконка",
|
||||||
|
"kind": "media",
|
||||||
|
"renderAs": "attr",
|
||||||
|
"group": "Dock"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "iconAlt",
|
||||||
|
"label": "Alt иконки",
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Dock"
|
"group": "Dock"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "dividerBefore",
|
||||||
|
"label": "Разделитель слева",
|
||||||
|
"kind": "boolean",
|
||||||
|
"renderAs": "boolean",
|
||||||
|
"group": "Dock"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "content.dock.demoFileLabel",
|
"path": "content.dock.demoFileLabel",
|
||||||
"label": "Dock: подпись демо-файла",
|
"label": "Dock: подпись демо-файла",
|
||||||
|
|
@ -283,13 +370,6 @@
|
||||||
"bodyHtml": "<span class=\"darker-70\">Деплой в один клик,</span> непрерывный пайплайн, мгновенный откат и быстрая <span class=\"darker-70\">доставка через CDN.</span>"
|
"bodyHtml": "<span class=\"darker-70\">Деплой в один клик,</span> непрерывный пайплайн, мгновенный откат и быстрая <span class=\"darker-70\">доставка через CDN.</span>"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"app": {
|
|
||||||
"iconAlt": "Иконка NODE.DC",
|
|
||||||
"logoAlt": "Логотип NODE.DC",
|
|
||||||
"release": "Бета-релиз: июль 2026",
|
|
||||||
"heading": "Ранний доступ",
|
|
||||||
"description": "Кастомный код, MCP и ИИ-агенты для Webflow в одном рабочем контуре."
|
|
||||||
},
|
|
||||||
"form": {
|
"form": {
|
||||||
"messagePlaceholder": "Сообщение (необязательно)",
|
"messagePlaceholder": "Сообщение (необязательно)",
|
||||||
"namePlaceholder": "Ваше имя*",
|
"namePlaceholder": "Ваше имя*",
|
||||||
|
|
@ -304,7 +384,17 @@
|
||||||
"failureHeading": "Не получилось",
|
"failureHeading": "Не получилось",
|
||||||
"failureTextHtml": "Что-то пошло не так. <br/>Попробуйте ещё раз чуть позже.<br/>"
|
"failureTextHtml": "Что-то пошло не так. <br/>Попробуйте ещё раз чуть позже.<br/>"
|
||||||
},
|
},
|
||||||
"legalHtml": "<a href=\"https://docs.google.com/document/d/1vME2w8ZB88HoH-JyvkkiL7exrcMfWhkERfFRlnIeZFA/edit?usp=sharing\" target=\"_blank\">Политика конфиденциальности</a> • <a href=\"https://docs.google.com/document/d/1vME2w8ZB88HoH-JyvkkiL7exrcMfWhkERfFRlnIeZFA/edit?usp=sharing\" target=\"_blank\">Политика cookie</a><br/>© — Все права защищены<br/>2026"
|
"legalHtml": "<a href=\"https://docs.google.com/document/d/1vME2w8ZB88HoH-JyvkkiL7exrcMfWhkERfFRlnIeZFA/edit?usp=sharing\" target=\"_blank\">Политика конфиденциальности</a> • <a href=\"https://docs.google.com/document/d/1vME2w8ZB88HoH-JyvkkiL7exrcMfWhkERfFRlnIeZFA/edit?usp=sharing\" target=\"_blank\">Политика cookie</a><br/>© — Все права защищены<br/>2026",
|
||||||
|
"accessCard": {
|
||||||
|
"enabled": true,
|
||||||
|
"iconSrc": "./assets/webflow/images/69ad964d951f9d17cc5a919e_logo-app.svg",
|
||||||
|
"iconAlt": "Иконка NODE.DC",
|
||||||
|
"wordmarkSrc": "./assets/webflow/images/69ad96a28b06b7552a339137_logtype.svg?v=node-dc",
|
||||||
|
"wordmarkAlt": "Логотип NODE.DC",
|
||||||
|
"release": "Бета-релиз: июль 2026",
|
||||||
|
"heading": "Ранний доступ",
|
||||||
|
"description": "Кастомный код, MCP и ИИ-агенты для Webflow в одном рабочем контуре."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"editableFields": [
|
"editableFields": [
|
||||||
{
|
{
|
||||||
|
|
@ -392,39 +482,61 @@
|
||||||
"group": "Тезисы первого экрана"
|
"group": "Тезисы первого экрана"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.iconAlt",
|
"path": "content.accessCard.enabled",
|
||||||
"label": "App: alt иконки",
|
"label": "Карточка доступа: отображение",
|
||||||
"kind": "text",
|
"kind": "boolean",
|
||||||
|
"renderAs": "boolean",
|
||||||
|
"group": "Карточка доступа",
|
||||||
|
"headerToggle": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "content.accessCard.iconSrc",
|
||||||
|
"label": "Карточка доступа: иконка",
|
||||||
|
"kind": "media",
|
||||||
"renderAs": "attr",
|
"renderAs": "attr",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.logoAlt",
|
"path": "content.accessCard.wordmarkSrc",
|
||||||
"label": "App: alt логотипа",
|
"label": "Карточка доступа: wordmark",
|
||||||
"kind": "text",
|
"kind": "media",
|
||||||
"renderAs": "attr",
|
"renderAs": "attr",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.release",
|
"path": "content.accessCard.iconAlt",
|
||||||
"label": "App: релиз",
|
"label": "Карточка доступа: alt иконки",
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.heading",
|
"path": "content.accessCard.wordmarkAlt",
|
||||||
"label": "App: заголовок",
|
"label": "Карточка доступа: alt логотипа",
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.description",
|
"path": "content.accessCard.release",
|
||||||
"label": "App: описание",
|
"label": "Карточка доступа: релиз",
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "content.accessCard.heading",
|
||||||
|
"label": "Карточка доступа: заголовок",
|
||||||
|
"kind": "text",
|
||||||
|
"renderAs": "text",
|
||||||
|
"group": "Карточка доступа"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "content.accessCard.description",
|
||||||
|
"label": "Карточка доступа: описание",
|
||||||
|
"kind": "textarea",
|
||||||
|
"renderAs": "text",
|
||||||
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.form.messagePlaceholder",
|
"path": "content.form.messagePlaceholder",
|
||||||
|
|
@ -2241,15 +2353,6 @@
|
||||||
"anchor": "waitlist",
|
"anchor": "waitlist",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"content": {
|
"content": {
|
||||||
"app": {
|
|
||||||
"iconSrc": "./assets/webflow/images/69ad964d951f9d17cc5a919e_logo-app.svg",
|
|
||||||
"iconAlt": "Иконка NODE.DC",
|
|
||||||
"wordmarkSrc": "./assets/webflow/images/69ad96a28b06b7552a339137_logtype.svg?v=node-dc",
|
|
||||||
"wordmarkAlt": "Логотип NODE.DC",
|
|
||||||
"release": "Бета-релиз: июль 2026",
|
|
||||||
"heading": "Ранний доступ",
|
|
||||||
"description": "Кастомный код, MCP и ИИ-агенты для Webflow в одном рабочем контуре."
|
|
||||||
},
|
|
||||||
"form": {
|
"form": {
|
||||||
"messagePlaceholder": "Сообщение (необязательно)",
|
"messagePlaceholder": "Сообщение (необязательно)",
|
||||||
"namePlaceholder": "Ваше имя*",
|
"namePlaceholder": "Ваше имя*",
|
||||||
|
|
@ -2502,57 +2605,75 @@
|
||||||
"iconSrcset": "./assets/webflow/images/69a9fb87bc56142371745926_txt-file-p-500.avif 500w, ./assets/webflow/images/69a9fb87bc56142371745926_txt-file.avif 1024w",
|
"iconSrcset": "./assets/webflow/images/69a9fb87bc56142371745926_txt-file-p-500.avif 500w, ./assets/webflow/images/69a9fb87bc56142371745926_txt-file.avif 1024w",
|
||||||
"iconAlt": "Иконка TXT-файла"
|
"iconAlt": "Иконка TXT-файла"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"accessCard": {
|
||||||
|
"enabled": true,
|
||||||
|
"iconSrc": "./assets/webflow/images/69ad964d951f9d17cc5a919e_logo-app.svg",
|
||||||
|
"iconAlt": "Иконка NODE.DC",
|
||||||
|
"wordmarkSrc": "./assets/webflow/images/69ad96a28b06b7552a339137_logtype.svg?v=node-dc",
|
||||||
|
"wordmarkAlt": "Логотип NODE.DC",
|
||||||
|
"release": "Бета-релиз: июль 2026",
|
||||||
|
"heading": "Ранний доступ",
|
||||||
|
"description": "Кастомный код, MCP и ИИ-агенты для Webflow в одном рабочем контуре."
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"editableFields": [
|
"editableFields": [
|
||||||
{
|
{
|
||||||
"path": "content.app.iconSrc",
|
"path": "content.accessCard.enabled",
|
||||||
"label": "Иконка приложения",
|
"label": "Карточка доступа: отображение",
|
||||||
|
"kind": "boolean",
|
||||||
|
"renderAs": "boolean",
|
||||||
|
"group": "Карточка доступа",
|
||||||
|
"headerToggle": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "content.accessCard.iconSrc",
|
||||||
|
"label": "Карточка доступа: иконка",
|
||||||
"kind": "media",
|
"kind": "media",
|
||||||
"renderAs": "attr",
|
"renderAs": "attr",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.iconAlt",
|
"path": "content.accessCard.wordmarkSrc",
|
||||||
"label": "Иконка: alt",
|
"label": "Карточка доступа: wordmark",
|
||||||
"kind": "text",
|
|
||||||
"renderAs": "attr",
|
|
||||||
"group": "Карточка раннего доступа"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "content.app.wordmarkSrc",
|
|
||||||
"label": "Wordmark",
|
|
||||||
"kind": "media",
|
"kind": "media",
|
||||||
"renderAs": "attr",
|
"renderAs": "attr",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.wordmarkAlt",
|
"path": "content.accessCard.iconAlt",
|
||||||
"label": "Wordmark: alt",
|
"label": "Карточка доступа: alt иконки",
|
||||||
"kind": "text",
|
|
||||||
"renderAs": "attr",
|
|
||||||
"group": "Карточка раннего доступа"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "content.app.release",
|
|
||||||
"label": "Релиз",
|
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.heading",
|
"path": "content.accessCard.wordmarkAlt",
|
||||||
"label": "Заголовок",
|
"label": "Карточка доступа: alt логотипа",
|
||||||
"kind": "text",
|
"kind": "text",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.app.description",
|
"path": "content.accessCard.release",
|
||||||
"label": "Описание",
|
"label": "Карточка доступа: релиз",
|
||||||
|
"kind": "text",
|
||||||
|
"renderAs": "text",
|
||||||
|
"group": "Карточка доступа"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "content.accessCard.heading",
|
||||||
|
"label": "Карточка доступа: заголовок",
|
||||||
|
"kind": "text",
|
||||||
|
"renderAs": "text",
|
||||||
|
"group": "Карточка доступа"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "content.accessCard.description",
|
||||||
|
"label": "Карточка доступа: описание",
|
||||||
"kind": "textarea",
|
"kind": "textarea",
|
||||||
"renderAs": "text",
|
"renderAs": "text",
|
||||||
"group": "Карточка раннего доступа"
|
"group": "Карточка доступа"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "content.form.messagePlaceholder",
|
"path": "content.form.messagePlaceholder",
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
||||||
<header id="hero" class="s h-screen"><div class="s-cont shorter"><div class="h-full between"><hgroup id="w-node-_42aec6af-ef19-ac2b-f604-ecf49ea735c6-43601640" data-module="hg" data-group="hero" class="hgroup main"><h1 class="main-h"><span class="darker-70">{{text:content.heading.muted}}</span><span>{{text:content.heading.main}}</span></h1><div class="h-txt">{{html:content.introHtml}}</div></hgroup><hgroup id="w-node-ddd50e0c-bdcb-417b-7dc2-7db828a06f46-43601640" data-module="hg" data-group="hero" class="to-left"><h2 class="main-h sm"><span class="darker-70">{{text:content.cards.0.muted}}</span><span>{{text:content.cards.0.main}}</span></h2><div class="h-txt">{{html:content.cards.0.bodyHtml}}</div></hgroup></div><div class="h-full between lower"><hgroup data-module="hg" data-group="hero"><h2 class="main-h sm"><span class="darker-70">{{text:content.cards.1.muted}}</span><span>{{text:content.cards.1.main}}</span></h2><div class="h-txt">{{html:content.cards.1.bodyHtml}}</div></hgroup><hgroup id="w-node-f82ddc17-0624-f069-9963-dda808a65750-43601640" data-module="hg" data-group="hero" class="to-right"><h2 class="main-h sm"><span class="darker-70">{{text:content.cards.2.muted}}</span><span>{{text:content.cards.2.main}}</span></h2><div class="h-txt">{{html:content.cards.2.bodyHtml}}</div></hgroup></div></div><div class="app-w"><div id="app-data" data-module="app" class="app-item data"><div data-draggable-area="" class="app-head preview"><div class="app-head-trigs"><button data-close="" arial-label="закрыть окно" class="circle opaque"><div class="x-icon"><div class="line full x-icon rot"></div><div class="line full x-icon"></div></div></button><div class="circle opaque"></div><div class="circle opaque"></div></div></div><div class="flex-v"><div class="flex-v _w-full"><img src="./assets/webflow/images/69ad964d951f9d17cc5a919e_logo-app.svg" loading="lazy" data-module="mouse-follow" alt="{{attr:content.app.iconAlt}}" class="logo app"/><div class="logo-lockup-w"><img src="./assets/webflow/images/69ad96a28b06b7552a339137_logtype.svg?v=node-dc" loading="eager" alt="{{attr:content.app.logoAlt}}" class="logo-size"/><div class="notice-txt sm">{{text:content.app.release}}</div></div></div><h3 data-module="text-wave" class="data-he">{{text:content.app.heading}}</h3><h2 class="data-he h2">{{text:content.app.description}}</h2><div class="form-element"><div data-module="form" class="form-w w-form"><form id="wf-form-sign-up" name="wf-form-sign-up" data-name="sign-up" method="get" data-form="" class="form" data-wf-page-id="69b2dc3d81980aaf43601640" data-wf-element-id="4c5736fb-5001-9c71-eaa8-239119d387ce"><textarea placeholder="{{attr:content.form.messagePlaceholder}}" maxlength="5000" id="Message" name="Message" data-name="Message" class="form-field taller w-input"></textarea><input class="form-field w-input" maxlength="256" name="name" data-name="Имя" placeholder="{{attr:content.form.namePlaceholder}}" type="text" id="Name" required=""/><div class="form-item"><input class="form-field w-input" maxlength="256" name="Email" data-name="Email" placeholder="{{attr:content.form.emailPlaceholder}}" type="email" id="Email" required=""/><button data-module="btn" class="form-btn"><div data-module="alpha" class="btn-bg"><div class="clip-12"><div>{{text:content.form.submitLabel}}</div></div></div></button></div></form><div data-success="" class="form-success"><h2 class="data-he sm">{{text:content.form.successHeading}}</h2><h2 class="data-he h2 xs">{{html:content.form.successTextHtml}}</h2><a data-module="btn" href="beta-apply.html" class="form-btn static w-inline-block"><div class="btn-bg gray"><div class="clip-12"><div>{{text:content.form.betaCtaLabel}}</div></div></div></a><h2 class="data-he h2 xs">{{html:content.form.founderIntroHtml}}</h2><a data-module="btn" href="founders.html" class="form-btn static w-inline-block"><div class="btn-bg"><div class="clip-12"><div>{{html:content.form.founderCtaHtml}}</div></div></div></a><link rel="prefetch" href="founders.html"/><div data-url="https://web.ssscript.app/api/founders-spots-remaining" data-module="founders-spots" class="pricing-sm darker-70 is--right">{{text:content.form.spotsLabel}}</div></div></div><div data-failure="" class="form-fail"><h2 class="data-he sm">{{text:content.form.failureHeading}}</h2><h2 class="data-he h2 xs">{{html:content.form.failureTextHtml}}</h2></div></div><div class="notice-txt">{{html:content.legalHtml}}</div></div></div></div></header>
|
<header id="hero" class="s h-screen"><div class="s-cont shorter"><div class="h-full between"><hgroup id="w-node-_42aec6af-ef19-ac2b-f604-ecf49ea735c6-43601640" data-module="hg" data-group="hero" class="hgroup main"><h1 class="main-h"><span class="darker-70">{{text:content.heading.muted}}</span><span>{{text:content.heading.main}}</span></h1><div class="h-txt">{{html:content.introHtml}}</div></hgroup><hgroup id="w-node-ddd50e0c-bdcb-417b-7dc2-7db828a06f46-43601640" data-module="hg" data-group="hero" class="to-left"><h2 class="main-h sm"><span class="darker-70">{{text:content.cards.0.muted}}</span><span>{{text:content.cards.0.main}}</span></h2><div class="h-txt">{{html:content.cards.0.bodyHtml}}</div></hgroup></div><div class="h-full between lower"><hgroup data-module="hg" data-group="hero"><h2 class="main-h sm"><span class="darker-70">{{text:content.cards.1.muted}}</span><span>{{text:content.cards.1.main}}</span></h2><div class="h-txt">{{html:content.cards.1.bodyHtml}}</div></hgroup><hgroup id="w-node-f82ddc17-0624-f069-9963-dda808a65750-43601640" data-module="hg" data-group="hero" class="to-right"><h2 class="main-h sm"><span class="darker-70">{{text:content.cards.2.muted}}</span><span>{{text:content.cards.2.main}}</span></h2><div class="h-txt">{{html:content.cards.2.bodyHtml}}</div></hgroup></div></div>{{#if content.accessCard.enabled}}<div class="app-w"><div id="app-data" data-module="app" class="app-item data"><div data-draggable-area="" class="app-head preview"><div class="app-head-trigs"><button data-close="" arial-label="закрыть окно" class="circle opaque"><div class="x-icon"><div class="line full x-icon rot"></div><div class="line full x-icon"></div></div></button><div class="circle opaque"></div><div class="circle opaque"></div></div></div><div class="flex-v"><div class="flex-v _w-full"><img src="{{attr:content.accessCard.iconSrc}}" loading="lazy" data-module="mouse-follow" alt="{{attr:content.accessCard.iconAlt}}" class="logo app"/><div class="logo-lockup-w"><img src="{{attr:content.accessCard.wordmarkSrc}}" loading="eager" alt="{{attr:content.accessCard.wordmarkAlt}}" class="logo-size"/><div class="notice-txt sm">{{text:content.accessCard.release}}</div></div></div><h3 data-module="text-wave" class="data-he">{{text:content.accessCard.heading}}</h3><h2 class="data-he h2">{{text:content.accessCard.description}}</h2><div class="form-element"><div data-module="form" class="form-w w-form"><form id="wf-form-sign-up" name="wf-form-sign-up" data-name="sign-up" method="get" data-form="" class="form" data-wf-page-id="69b2dc3d81980aaf43601640" data-wf-element-id="4c5736fb-5001-9c71-eaa8-239119d387ce"><textarea placeholder="{{attr:content.form.messagePlaceholder}}" maxlength="5000" id="Message" name="Message" data-name="Message" class="form-field taller w-input"></textarea><input class="form-field w-input" maxlength="256" name="name" data-name="Имя" placeholder="{{attr:content.form.namePlaceholder}}" type="text" id="Name" required=""/><div class="form-item"><input class="form-field w-input" maxlength="256" name="Email" data-name="Email" placeholder="{{attr:content.form.emailPlaceholder}}" type="email" id="Email" required=""/><button data-module="btn" class="form-btn"><div data-module="alpha" class="btn-bg"><div class="clip-12"><div>{{text:content.form.submitLabel}}</div></div></div></button></div></form><div data-success="" class="form-success"><h2 class="data-he sm">{{text:content.form.successHeading}}</h2><h2 class="data-he h2 xs">{{html:content.form.successTextHtml}}</h2><a data-module="btn" href="beta-apply.html" class="form-btn static w-inline-block"><div class="btn-bg gray"><div class="clip-12"><div>{{text:content.form.betaCtaLabel}}</div></div></div></a><h2 class="data-he h2 xs">{{html:content.form.founderIntroHtml}}</h2><a data-module="btn" href="founders.html" class="form-btn static w-inline-block"><div class="btn-bg"><div class="clip-12"><div>{{html:content.form.founderCtaHtml}}</div></div></div></a><link rel="prefetch" href="founders.html"/><div data-url="https://web.ssscript.app/api/founders-spots-remaining" data-module="founders-spots" class="pricing-sm darker-70 is--right">{{text:content.form.spotsLabel}}</div></div></div><div data-failure="" class="form-fail"><h2 class="data-he sm">{{text:content.form.failureHeading}}</h2><h2 class="data-he h2 xs">{{html:content.form.failureTextHtml}}</h2></div></div><div class="notice-txt">{{html:content.legalHtml}}</div></div></div></div>{{/if}}</header>
|
||||||
|
|
@ -1 +1 @@
|
||||||
<section id="{{attr:anchor}}" class="s h-screen"><div class="app-w"><div id="app-data" data-module="app" class="app-item data"><div data-draggable-area="" class="app-head preview"><div class="app-head-trigs"><button data-close="" aria-label="закрыть" class="circle opaque"><div class="x-icon"><div class="line full x-icon rot"></div><div class="line full x-icon"></div></div></button><div class="circle opaque"></div><div class="circle opaque"></div></div></div><div class="flex-v"><div class="flex-v _w-full"><img src="{{attr:content.app.iconSrc}}" loading="eager" data-module="mouse-follow" alt="{{attr:content.app.iconAlt}}" class="logo app"/><div class="logo-lockup-w"><img src="{{attr:content.app.wordmarkSrc}}" loading="eager" alt="{{attr:content.app.wordmarkAlt}}" class="logo-size"/><div class="notice-txt sm">{{text:content.app.release}}</div></div></div><h1 data-module="text-wave" class="data-he">{{text:content.app.heading}}</h1><h2 class="data-he h2">{{text:content.app.description}}</h2><div class="form-element"><div data-module="form" class="form-w w-form"><form id="wf-form-sign-up" name="wf-form-sign-up" data-name="sign-up" method="get" data-form="" class="form" data-wf-page-id="69b2dc3d81980aaf43601640" data-wf-element-id="3167f865-849c-497b-197f-50d61e82127f"><textarea placeholder="{{attr:content.form.messagePlaceholder}}" maxlength="5000" id="Message" name="Message" data-name="Message" class="form-field taller w-input"></textarea><input class="form-field w-input" maxlength="256" name="name" data-name="Имя" placeholder="{{attr:content.form.namePlaceholder}}" type="text" id="Name" required=""/><div class="form-item"><input class="form-field w-input" maxlength="256" name="Email" data-name="Email" placeholder="{{attr:content.form.emailPlaceholder}}" type="email" id="Email" required=""/><button data-module="btn" class="form-btn"><div data-module="alpha" class="btn-bg"><div class="clip-12"><div>{{text:content.form.submitLabel}}</div></div></div></button></div></form><div data-success="" class="form-success"><h2 class="data-he sm">{{text:content.form.successHeading}}</h2><h2 class="data-he h2 xs">{{html:content.form.successTextHtml}}</h2><a data-module="btn" href="{{attr:content.form.betaHref}}" class="form-btn static w-inline-block"><div data-module="alpha" class="btn-bg gray"><div class="clip-12"><div>{{text:content.form.betaCtaLabel}}</div></div></div></a></div></div></div><div class="notice-txt">{{html:content.legalHtml}}</div></div></div></div><div class="waitlist-coll"><div class="folder-w txts w-dyn-list"><div role="list" class="folder-w txts-windows w-dyn-items">{{#each content.files}}<div role="listitem" class="app-item-dot w-dyn-item"><div id="{{attr:id}}" data-module="app" data-start="closed" class="app-item txt"><div data-draggable-area="" class="app-head"><div class="app-head-trigs"><button data-close="" class="circle red"></button><div class="circle yellow"></div><div class="circle green"></div></div><div class="app-name-w"><img src="{{attr:iconSrc}}" loading="lazy" sizes="(max-width: 1024px) 100vw, 1024px" srcset="{{attr:iconSrcset}}" alt="{{attr:iconAlt}}" class="app-icon"/><div class="app-name-txt">{{text:name}}</div></div></div><div class="text-editable"><div data-module="editable-text" class="txt-text-block editable">{{html:bodyHtml}}</div></div></div></div>{{/each}}</div></div><div class="collection-list-wrapper-3 w-dyn-list"><div data-form="duplicate" role="list" class="folders txts w-dyn-items">{{#each content.files}}<div role="listitem" class="collection-item-2 w-dyn-item"><div tabindex="0" data-target="{{attr:id}}" data-module="apple-icon" class="folder"><img class="image" src="{{attr:iconSrc}}" alt="{{attr:iconAlt}}" sizes="(max-width: 1024px) 100vw, 1024px" data-icon="" loading="eager" srcset="{{attr:iconSrcset}}"/><div data-txt="" class="text-ho"><div class="folder-title">{{text:name}}</div><div class="folder-title lower">.txt</div></div></div></div>{{/each}}</div></div></div></section>
|
<section id="{{attr:anchor}}" class="s h-screen">{{#if content.accessCard.enabled}}<div class="app-w"><div id="app-data" data-module="app" class="app-item data"><div data-draggable-area="" class="app-head preview"><div class="app-head-trigs"><button data-close="" aria-label="закрыть" class="circle opaque"><div class="x-icon"><div class="line full x-icon rot"></div><div class="line full x-icon"></div></div></button><div class="circle opaque"></div><div class="circle opaque"></div></div></div><div class="flex-v"><div class="flex-v _w-full"><img src="{{attr:content.accessCard.iconSrc}}" loading="eager" data-module="mouse-follow" alt="{{attr:content.accessCard.iconAlt}}" class="logo app"/><div class="logo-lockup-w"><img src="{{attr:content.accessCard.wordmarkSrc}}" loading="eager" alt="{{attr:content.accessCard.wordmarkAlt}}" class="logo-size"/><div class="notice-txt sm">{{text:content.accessCard.release}}</div></div></div><h1 data-module="text-wave" class="data-he">{{text:content.accessCard.heading}}</h1><h2 class="data-he h2">{{text:content.accessCard.description}}</h2><div class="form-element"><div data-module="form" class="form-w w-form"><form id="wf-form-sign-up" name="wf-form-sign-up" data-name="sign-up" method="get" data-form="" class="form" data-wf-page-id="69b2dc3d81980aaf43601640" data-wf-element-id="3167f865-849c-497b-197f-50d61e82127f"><textarea placeholder="{{attr:content.form.messagePlaceholder}}" maxlength="5000" id="Message" name="Message" data-name="Message" class="form-field taller w-input"></textarea><input class="form-field w-input" maxlength="256" name="name" data-name="Имя" placeholder="{{attr:content.form.namePlaceholder}}" type="text" id="Name" required=""/><div class="form-item"><input class="form-field w-input" maxlength="256" name="Email" data-name="Email" placeholder="{{attr:content.form.emailPlaceholder}}" type="email" id="Email" required=""/><button data-module="btn" class="form-btn"><div data-module="alpha" class="btn-bg"><div class="clip-12"><div>{{text:content.form.submitLabel}}</div></div></div></button></div></form><div data-success="" class="form-success"><h2 class="data-he sm">{{text:content.form.successHeading}}</h2><h2 class="data-he h2 xs">{{html:content.form.successTextHtml}}</h2><a data-module="btn" href="{{attr:content.form.betaHref}}" class="form-btn static w-inline-block"><div data-module="alpha" class="btn-bg gray"><div class="clip-12"><div>{{text:content.form.betaCtaLabel}}</div></div></div></a></div></div></div><div class="notice-txt">{{html:content.legalHtml}}</div></div></div></div>{{/if}}<div class="waitlist-coll"><div class="folder-w txts w-dyn-list"><div role="list" class="folder-w txts-windows w-dyn-items">{{#each content.files}}<div role="listitem" class="app-item-dot w-dyn-item"><div id="{{attr:id}}" data-module="app" data-start="closed" class="app-item txt"><div data-draggable-area="" class="app-head"><div class="app-head-trigs"><button data-close="" class="circle red"></button><div class="circle yellow"></div><div class="circle green"></div></div><div class="app-name-w"><img src="{{attr:iconSrc}}" loading="lazy" sizes="(max-width: 1024px) 100vw, 1024px" srcset="{{attr:iconSrcset}}" alt="{{attr:iconAlt}}" class="app-icon"/><div class="app-name-txt">{{text:name}}</div></div></div><div class="text-editable"><div data-module="editable-text" class="txt-text-block editable">{{html:bodyHtml}}</div></div></div></div>{{/each}}</div></div><div class="collection-list-wrapper-3 w-dyn-list"><div data-form="duplicate" role="list" class="folders txts w-dyn-items">{{#each content.files}}<div role="listitem" class="collection-item-2 w-dyn-item"><div tabindex="0" data-target="{{attr:id}}" data-module="apple-icon" class="folder"><img class="image" src="{{attr:iconSrc}}" alt="{{attr:iconAlt}}" sizes="(max-width: 1024px) 100vw, 1024px" data-icon="" loading="eager" srcset="{{attr:iconSrcset}}"/><div data-txt="" class="text-ho"><div class="folder-title">{{text:name}}</div><div class="folder-title lower">.txt</div></div></div></div>{{/each}}</div></div></div></section>
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -37,6 +37,11 @@ function getByPath(source, path) {
|
||||||
return path.split(".").reduce((value, key) => value?.[key], source);
|
return path.split(".").reduce((value, key) => value?.[key], source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isTruthy(value) {
|
||||||
|
if (Array.isArray(value)) return value.length > 0;
|
||||||
|
return Boolean(value);
|
||||||
|
}
|
||||||
|
|
||||||
function escapeHtml(value) {
|
function escapeHtml(value) {
|
||||||
return String(value)
|
return String(value)
|
||||||
.replaceAll("&", "&")
|
.replaceAll("&", "&")
|
||||||
|
|
@ -83,6 +88,24 @@ function renderTemplateTokens(html, block) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderIfBlocks(html, block) {
|
||||||
|
let rendered = html;
|
||||||
|
let previous = null;
|
||||||
|
|
||||||
|
while (rendered !== previous) {
|
||||||
|
previous = rendered;
|
||||||
|
rendered = rendered.replace(
|
||||||
|
/\{\{#if\s+([a-zA-Z0-9_.-]+)\s*\}\}((?:(?!\{\{#if|\{\{\/if\}\})[\s\S])*)\{\{\/if\}\}/g,
|
||||||
|
(_match, path, innerHtml) => {
|
||||||
|
const value = getByPath(block, path);
|
||||||
|
return isTruthy(value) ? renderTemplate(innerHtml, block) : "";
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rendered;
|
||||||
|
}
|
||||||
|
|
||||||
function renderEachBlocks(html, block) {
|
function renderEachBlocks(html, block) {
|
||||||
return html.replace(/\{\{#each\s+([a-zA-Z0-9_.-]+)\s*\}\}([\s\S]*?)\{\{\/each\}\}/g, (_match, path, innerHtml) => {
|
return html.replace(/\{\{#each\s+([a-zA-Z0-9_.-]+)\s*\}\}([\s\S]*?)\{\{\/each\}\}/g, (_match, path, innerHtml) => {
|
||||||
const items = getByPath(block, path);
|
const items = getByPath(block, path);
|
||||||
|
|
@ -91,10 +114,14 @@ function renderEachBlocks(html, block) {
|
||||||
throw new Error(`Each token "${path}" in block "${block.id}" must point to an array`);
|
throw new Error(`Each token "${path}" in block "${block.id}" must point to an array`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return items.map((item) => renderTemplateTokens(innerHtml, item ?? {})).join("");
|
return items.map((item) => renderTemplate(innerHtml, item ?? {})).join("");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderTemplate(html, block) {
|
||||||
|
return renderTemplateTokens(renderIfBlocks(renderEachBlocks(html, block), block), block);
|
||||||
|
}
|
||||||
|
|
||||||
function scopeDuplicateIds(html, scope) {
|
function scopeDuplicateIds(html, scope) {
|
||||||
const ids = [...html.matchAll(/\sid="([^"]+)"/g)].map((match) => match[1]);
|
const ids = [...html.matchAll(/\sid="([^"]+)"/g)].map((match) => match[1]);
|
||||||
if (!ids.length) {
|
if (!ids.length) {
|
||||||
|
|
@ -124,7 +151,7 @@ async function renderBlock(block, renderCounts) {
|
||||||
const renderCount = renderCounts.get(templateName) ?? 0;
|
const renderCount = renderCounts.get(templateName) ?? 0;
|
||||||
renderCounts.set(templateName, renderCount + 1);
|
renderCounts.set(templateName, renderCount + 1);
|
||||||
|
|
||||||
const html = renderTemplateTokens(renderEachBlocks(await readTemplate(templateName), block), block);
|
const html = renderTemplate(await readTemplate(templateName), block);
|
||||||
|
|
||||||
if (renderCount === 0 && block.scopeIds !== true) {
|
if (renderCount === 0 && block.scopeIds !== true) {
|
||||||
return html;
|
return html;
|
||||||
|
|
@ -144,7 +171,7 @@ async function renderRegion(region) {
|
||||||
return blocks.join("");
|
return blocks.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
let output = renderTemplateTokens(renderEachBlocks(shell, { id: page.slug, ...page }), { id: page.slug, ...page });
|
let output = renderTemplate(shell, { id: page.slug, ...page });
|
||||||
for (const region of page.regions ?? []) {
|
for (const region of page.regions ?? []) {
|
||||||
const token = REGION_TOKEN[region.id];
|
const token = REGION_TOKEN[region.id];
|
||||||
if (!token) {
|
if (!token) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue