788 lines
23 KiB
Plaintext
788 lines
23 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "750a3ea8",
|
||
"metadata": {},
|
||
"source": [
|
||
"### git hook"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "50691f34",
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "shellscript"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"%%bash\n",
|
||
"cat > .git/hooks/pre-commit <<'EOF'\n",
|
||
"#!/usr/bin/env bash\n",
|
||
"set -euo pipefail\n",
|
||
"\n",
|
||
"FILES=$(git diff --cached --name-only -- '*.ipynb' || true)\n",
|
||
"if [ -z \"$FILES\" ]; then\n",
|
||
" exit 0\n",
|
||
"fi\n",
|
||
"\n",
|
||
"export FILES\n",
|
||
"python3 - <<'PY'\n",
|
||
"import json, subprocess, os\n",
|
||
"from pathlib import Path\n",
|
||
"\n",
|
||
"files = os.environ.get(\"FILES\",\"\").splitlines()\n",
|
||
"for f in files:\n",
|
||
" p = Path(f)\n",
|
||
" nb = json.loads(p.read_text(encoding=\"utf-8\"))\n",
|
||
" for cell in nb.get(\"cells\", []):\n",
|
||
" if cell.get(\"cell_type\") == \"code\":\n",
|
||
" cell[\"outputs\"] = []\n",
|
||
" cell[\"execution_count\"] = None\n",
|
||
" p.write_text(json.dumps(nb, ensure_ascii=False, indent=1) + \"\\n\", encoding=\"utf-8\")\n",
|
||
"\n",
|
||
"subprocess.run([\"git\",\"add\"] + files, check=True)\n",
|
||
"PY\n",
|
||
"EOF\n",
|
||
"\n",
|
||
"chmod +x .git/hooks/pre-commit\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "2657898e",
|
||
"metadata": {},
|
||
"source": [
|
||
"### env setup"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "d0c8af1f",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# ignore\n",
|
||
"import os\n",
|
||
"\n",
|
||
"def load_env(file_path):\n",
|
||
" \"\"\"Загружает переменные окружения из файла .env с поддержкой булевых значений.\"\"\"\n",
|
||
" with open(file_path) as f:\n",
|
||
" for line in f:\n",
|
||
" line = line.strip()\n",
|
||
" if not line or line.startswith('#'):\n",
|
||
" continue\n",
|
||
" \n",
|
||
" try:\n",
|
||
" key, value = line.split('=', 1)\n",
|
||
" key = key.strip()\n",
|
||
" value = value.strip('\"\\' ')\n",
|
||
" # Преобразование строковых значений в булевые\n",
|
||
" if value.lower() in ['true', '1', 'yes']:\n",
|
||
" value = True\n",
|
||
" elif value.lower() in ['false', '0', 'no']:\n",
|
||
" value = False\n",
|
||
" \n",
|
||
" # Проверка, что ключ не пустой\n",
|
||
" if key:\n",
|
||
" os.environ[key] = str(value) # Храним как строку\n",
|
||
" except ValueError:\n",
|
||
" print(f\"Ошибка: неверный формат в строке '{line}'\")\n",
|
||
"\n",
|
||
"try:\n",
|
||
" load_env('env')\n",
|
||
"except:\n",
|
||
" load_env('env_example')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "d0947a0b",
|
||
"metadata": {},
|
||
"source": [
|
||
"### setup"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "63d3463e",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!kubectl create secret docker-registry regcred --docker-server=$REGISTRY_HOST --docker-username='user' --docker-password=$REGISTRY_PASS --docker-email=\"$(git config --global --get user.email)\""
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "ff732fa4",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!docker login $REGISTRY_HOST -u 'user' -p $REGISTRY_PASS"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "75172234",
|
||
"metadata": {},
|
||
"source": [
|
||
"### pre pull"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "ff55c6cf",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!docker pull $REGISTRY_HOST/python:3.13-alpine"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "34437078",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!docker pull $REGISTRY_HOST/python:3.13-slim"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "74d7ec58",
|
||
"metadata": {},
|
||
"source": [
|
||
"### build (ipynb -> py)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "2809c42c",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"import json\n",
|
||
"import os\n",
|
||
"\n",
|
||
"IGNORE = ['build.ipynb', 'data.ipynb']\n",
|
||
"def build():\n",
|
||
" for _filename in [i for i in os.listdir() if 'ipynb' in i and i not in IGNORE]:\n",
|
||
" filename_without_extension = os.path.splitext(os.path.basename(_filename))[0]\n",
|
||
"\n",
|
||
" with open(f'{filename_without_extension}.ipynb', 'r') as f:\n",
|
||
" data = f.read()\n",
|
||
" main = json.loads(data)\n",
|
||
" app = []\n",
|
||
" for cell in main['cells']:\n",
|
||
" if '## Run' in cell['source']:\n",
|
||
" break\n",
|
||
" if '# ignore\\n' in cell['source']:\n",
|
||
" continue\n",
|
||
" app.append(''.join(cell['source']))\n",
|
||
"\n",
|
||
" with open(f'{filename_without_extension}.py', 'w') as f:\n",
|
||
" f.write('\\n\\n'.join(app))\n",
|
||
"\n",
|
||
"build()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "37eabaef",
|
||
"metadata": {},
|
||
"source": [
|
||
"### quick"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "d62a9efa",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!docker build -t $REGISTRY_HOST/avtopost-api:latest ."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "fa6370f6",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!docker image push $REGISTRY_HOST/avtopost-api:latest"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "0309c9b3",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!kubectl rollout restart deployments/kultmost-api -n kultmost"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "b1c2c66c",
|
||
"metadata": {},
|
||
"source": [
|
||
"### rollout restart celery"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "8c359276",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!kubectl rollout restart deployments/vk-celery -n kultmost"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "a75776f0",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!kubectl rollout restart deployments/email-celery -n kultmost"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "97d2a6d6",
|
||
"metadata": {},
|
||
"source": [
|
||
"### k8s"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "496f4cc7",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!docker build -t $REGISTRY_HOST/ssh-tunnel:2 tunnel/\n",
|
||
"!docker push $REGISTRY_HOST/ssh-tunnel:2"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "4419ce48",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!kubectl create secret generic ssh-id-rsa --from-file=id_rsa=/home/user/.ssh/id_rsa -n kultmost"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "aa7b001d",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!kubectl apply -n kultmost -f k8s/stunnel.yaml"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "c472ba3a",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!envsubst < k8s/prod.yaml | kubectl apply -n kultmost -f -"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "b8579fa2",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!envsubst < k8s/kultmost.yaml | kubectl apply -n kultmost -f -"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "01c430ae",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!envsubst < k8s/mq.yaml | kubectl apply -n kultmost -f -"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "598e0bc3",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!envsubst < k8s/gitea.yaml | kubectl apply -n gitea -f -"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "35a1cf25",
|
||
"metadata": {},
|
||
"source": [
|
||
"### tg yaml"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "d4139bf6",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!echo \"kubectl create secret docker-registry regcred --docker-server=$REGISTRY_HOST --docker-username='user' --docker-password=$REGISTRY_PASS --docker-email=\\\"$(git config --global --get user.email)\\\" && clear && history -c\""
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "fa824c3b",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!echo \"kubectl apply -f - <<'EOF'\"\n",
|
||
"!echo \"$(envsubst < k8s/tg.yaml)\"\n",
|
||
"!echo \"EOF\"\n",
|
||
"!echo \"clear\"\n",
|
||
"!echo \"history -c\"\n",
|
||
"!echo \"\""
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "0a429fca",
|
||
"metadata": {},
|
||
"source": [
|
||
"### rsync"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "ff536fb8",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"#!rsync -avz ./static kultmost:/var/www/static/"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "3aba3afb",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!rsync -avh --exclude-from=.rsyncignore . /app/"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "11c03751",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!rsync -avh --exclude-from=.rsyncignore --exclude=.venv/ /app/ kultmost:/app/"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "0474516d",
|
||
"metadata": {},
|
||
"source": [
|
||
"### restart fastapi"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "001d41fe",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!ssh kultmost 'systemctl restart fastapi.service'\n",
|
||
"!ssh kultmost 'systemctl status fastapi.service'"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "004c5bea",
|
||
"metadata": {},
|
||
"source": [
|
||
"### restart celery"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "3cc3fd1c",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!ssh kultmost 'systemctl restart vk-celery.service'\n",
|
||
"!ssh kultmost 'systemctl status vk-celery.service'"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "68340709",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!ssh kultmost 'systemctl restart email-celery.service'\n",
|
||
"!ssh kultmost 'systemctl status email-celery.service'"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "1a723121",
|
||
"metadata": {},
|
||
"source": [
|
||
"### nginx"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "003f0324",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# !scp kultmost:/etc/nginx/sites-enabled/default nginx_conf "
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "dbfffccd",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!scp nginx_conf kultmost:/tmp/default\n",
|
||
"!ssh kultmost 'sudo mv /tmp/default /etc/nginx/sites-enabled/default'\n",
|
||
"!ssh kultmost 'nginx -t'"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "f7d27751",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"!ssh kultmost 'nginx -s reload'"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "a26138d2",
|
||
"metadata": {},
|
||
"source": [
|
||
"### microk8s"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "e10becc3",
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "shellscript"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"sudo snap install microk8s --classic\n",
|
||
"sudo usermod -a -G microk8s $USER\n",
|
||
"newgrp microk8s\n",
|
||
"microk8s status"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "b0761e24",
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "shellscript"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"curl -LO https://cert-manager.io/public-keys/cert-manager-keyring-2021-09-20-1020CF3C033D4F35BAE1C19E1226061C665DF13E.gpg\n",
|
||
"helm install cert-manager oci://quay.io/jetstack/charts/cert-manager --version v1.20.2 --namespace cert-manager --create-namespace --verify --keyring ./cert-manager-keyring-2021-09-20-1020CF3C033D4F35BAE1C19E1226061C665DF13E.gpg --set crds.enabled=true\n",
|
||
"microk8s helm install cert-manager oci://quay.io/jetstack/charts/cert-manager --version v1.20.2 --namespace cert-manager --create-namespace --verify --keyring ./cert-manager-keyring-2021-09-20-1020CF3C033D4F35BAE1C19E1226061C665DF13E.gpg --set crds.enabled=true"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "622f00a3",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Figma"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "8fe7cf08",
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "javascript"
|
||
}
|
||
},
|
||
"outputs": [],
|
||
"source": [
|
||
"m1 = figma.currentPage.children[0]\n",
|
||
"HTML_TAGS = new Set(['a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr']);\n",
|
||
"\n",
|
||
"function isHtmlTag(name) {\n",
|
||
" return typeof name === 'string' && HTML_TAGS.has(name.toLowerCase());\n",
|
||
"}\n",
|
||
"\n",
|
||
"function randomString5() { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let s = ''; for (let i = 0; i < 5; i++) s += chars.charAt(Math.floor(Math.random() * chars.length)); return s; }\n",
|
||
"\n",
|
||
"pc = m1.children[0]\n",
|
||
"tablet = m1.children[1]\n",
|
||
"mob = m1.children[2]\n",
|
||
"console.log(pc.name, tablet.name, mob.name)\n",
|
||
"// helpers (примерные реализации, если у вас другие — замените)\n",
|
||
"function randomString5() {\n",
|
||
" return Math.random().toString(36).slice(2, 7);\n",
|
||
"}\n",
|
||
"function isHtmlTag(name) {\n",
|
||
" return typeof name === 'string' && /^[a-zA-Z][\\w-]*$/.test(name);\n",
|
||
"}\n",
|
||
"function cssObjectToString(obj) {\n",
|
||
" return Object.entries(obj || {})\n",
|
||
" .map(([k, v]) => `${k.replace(/[A-Z]/g, m => '-' + m.toLowerCase())}: ${v};`)\n",
|
||
" .join(' ');\n",
|
||
"}\n",
|
||
"// helper: конвертирует объект CSS в строку\n",
|
||
"function cssObjectToString(obj = {}) {\n",
|
||
" return Object.entries(obj)\n",
|
||
" .map(([k, v]) => `${k}: ${v};`)\n",
|
||
" .join(' ');\n",
|
||
"}\n",
|
||
"\n",
|
||
"async function element_create(pc_node, tablet_node, mob_node) {\n",
|
||
" const cssRules = [];\n",
|
||
"\n",
|
||
" const randomString5 = () => Math.random().toString(36).substring(2, 5);\n",
|
||
"\n",
|
||
" function nodeExists(node, idx) {\n",
|
||
" return node && Array.isArray(node.children) && node.children[idx];\n",
|
||
" }\n",
|
||
"\n",
|
||
" async function getStyle(node) {\n",
|
||
" if (!node) return {};\n",
|
||
" if (typeof node.getCSSAsync === 'function') {\n",
|
||
" return await node.getCSSAsync();\n",
|
||
" }\n",
|
||
" return node.style || {};\n",
|
||
" }\n",
|
||
"\n",
|
||
" function cssObjectToString(cssObj) {\n",
|
||
" if (!cssObj || typeof cssObj !== 'object') return '';\n",
|
||
" return Object.entries(cssObj)\n",
|
||
" .map(([prop, value]) => `${prop}: ${value};`)\n",
|
||
" .sort()\n",
|
||
" .join(' ');\n",
|
||
" }\n",
|
||
"\n",
|
||
" function findExistingClass(pcBody, tableBody, mobBody) {\n",
|
||
" for (const r of cssRules) {\n",
|
||
" if (r.pcBody === pcBody && \n",
|
||
" r.tableBody === tableBody && \n",
|
||
" r.mobBody === mobBody) {\n",
|
||
" return r.cls;\n",
|
||
" }\n",
|
||
" }\n",
|
||
" return null;\n",
|
||
" }\n",
|
||
"\n",
|
||
" async function build(pcN, tabN, mobN) {\n",
|
||
" if (!pcN?.name) return document.createElement('div');\n",
|
||
"\n",
|
||
" const tagName = pcN.name.replace(/Desktop|desktop/gi, 'section').toLowerCase();\n",
|
||
" const el = document.createElement(tagName);\n",
|
||
"\n",
|
||
" if (pcN.characters != null) {\n",
|
||
" el.textContent = pcN.characters;\n",
|
||
" }\n",
|
||
"\n",
|
||
" // Рекурсия по детям\n",
|
||
" if (Array.isArray(pcN.children) && pcN.children.length) {\n",
|
||
" for (let i = 0; i < pcN.children.length; i++) {\n",
|
||
" const pcChild = pcN.children[i];\n",
|
||
" const tabChild = nodeExists(tabN, i) ? tabN.children[i] : pcChild;\n",
|
||
" const mobChild = nodeExists(mobN, i) ? mobN.children[i] : pcChild;\n",
|
||
"\n",
|
||
" if (typeof isHtmlTag === 'function' && isHtmlTag(pcChild.name)) {\n",
|
||
" const childEl = await build(pcChild, tabChild, mobChild);\n",
|
||
" el.appendChild(childEl);\n",
|
||
" }\n",
|
||
" }\n",
|
||
" }\n",
|
||
"\n",
|
||
" const pcCss = await getStyle(pcN);\n",
|
||
" const tabCss = await getStyle(tabN);\n",
|
||
" const mobCss = await getStyle(mobN);\n",
|
||
"\n",
|
||
" const pcBody = cssObjectToString(pcCss);\n",
|
||
" const tableBody = cssObjectToString(tabCss);\n",
|
||
" const mobBody = cssObjectToString(mobCss);\n",
|
||
"\n",
|
||
" let cls = findExistingClass(pcBody, tableBody, mobBody);\n",
|
||
"\n",
|
||
" if (!cls) {\n",
|
||
" cls = 'c_' + randomString5();\n",
|
||
" const baseCls = 'b_' + randomString5();\n",
|
||
"\n",
|
||
" cssRules.push({\n",
|
||
" cls,\n",
|
||
" baseCls,\n",
|
||
" pcBody,\n",
|
||
" tableBody,\n",
|
||
" mobBody,\n",
|
||
" baseStyle: '' // заполним ниже\n",
|
||
" });\n",
|
||
" }\n",
|
||
"\n",
|
||
" // Добавляем оба класса к элементу\n",
|
||
" el.classList.add(cls);\n",
|
||
" // base-класс добавим только если есть общие стили (позже)\n",
|
||
"\n",
|
||
" return el;\n",
|
||
" }\n",
|
||
"\n",
|
||
" const rootEl = await build(pc_node, tablet_node, mob_node);\n",
|
||
"\n",
|
||
" // === Определяем общие стили и различия ===\n",
|
||
" const baseBlockParts = [];\n",
|
||
"\n",
|
||
" for (const rule of cssRules) {\n",
|
||
" const { cls, baseCls, pcBody, tableBody, mobBody } = rule;\n",
|
||
"\n",
|
||
" let baseStyle = '';\n",
|
||
"\n",
|
||
" // Определяем, есть ли общие стили\n",
|
||
" if (pcBody && pcBody === tableBody && tableBody === mobBody) {\n",
|
||
" baseStyle = pcBody;\n",
|
||
" } else if (pcBody && pcBody === tableBody) {\n",
|
||
" baseStyle = pcBody; // desktop + tablet одинаковые\n",
|
||
" } else if (pcBody && pcBody === mobBody) {\n",
|
||
" baseStyle = pcBody;\n",
|
||
" } else if (tableBody && tableBody === mobBody) {\n",
|
||
" baseStyle = tableBody;\n",
|
||
" }\n",
|
||
"\n",
|
||
" rule.baseStyle = baseStyle;\n",
|
||
"\n",
|
||
" // Добавляем base-класс только если есть общие стили\n",
|
||
" if (baseStyle) {\n",
|
||
" baseBlockParts.push(`.${baseCls} { ${baseStyle} }`);\n",
|
||
" \n",
|
||
" // Находим все элементы с этим cls и добавляем им baseCls\n",
|
||
" // (поскольку build уже завершён, делаем это через DOM)\n",
|
||
" rootEl.querySelectorAll(`.${cls}`).forEach(el => {\n",
|
||
" el.classList.add(baseCls);\n",
|
||
" });\n",
|
||
" }\n",
|
||
"\n",
|
||
" // Различия для медиа-запросов (только то, что отличается от base)\n",
|
||
" rule.mobDiff = (mobBody && mobBody !== baseStyle) ? `.${cls} { ${mobBody} }` : '';\n",
|
||
" rule.tableDiff = (tableBody && tableBody !== baseStyle) ? `.${cls} { ${tableBody} }` : '';\n",
|
||
" rule.pcDiff = (pcBody && pcBody !== baseStyle) ? `.${cls} { ${pcBody} }` : '';\n",
|
||
" }\n",
|
||
"\n",
|
||
" const baseBlock = baseBlockParts.join('\\n');\n",
|
||
" const mobBlock = cssRules.map(r => r.mobDiff).filter(Boolean).join('\\n');\n",
|
||
" const tableBlock = cssRules.map(r => r.tableDiff).filter(Boolean).join('\\n');\n",
|
||
" const pcBlock = cssRules.map(r => r.pcDiff).filter(Boolean).join('\\n');\n",
|
||
"\n",
|
||
" // === Создаём <style> ===\n",
|
||
" const styleEl = document.createElement('style');\n",
|
||
" styleEl.type = 'text/css';\n",
|
||
" styleEl.textContent = `\n",
|
||
"/* Base (common) styles */\n",
|
||
"${baseBlock}\n",
|
||
"\n",
|
||
"/* Mobile */\n",
|
||
"@media (max-width: 699px) {\n",
|
||
"${mobBlock}\n",
|
||
"}\n",
|
||
"\n",
|
||
"/* Tablet */\n",
|
||
"@media (min-width: 700px) and (max-width: 900px) {\n",
|
||
"${tableBlock}\n",
|
||
"}\n",
|
||
"\n",
|
||
"/* Desktop */\n",
|
||
"@media (min-width: 901px) {\n",
|
||
"${pcBlock}\n",
|
||
"}\n",
|
||
"`.trim();\n",
|
||
"\n",
|
||
" const ret = document.createElement('div');\n",
|
||
" ret.appendChild(rootEl);\n",
|
||
" ret.appendChild(styleEl);\n",
|
||
"\n",
|
||
" return ret;\n",
|
||
"}\n",
|
||
"\n",
|
||
"sss = await element_create(pc, tablet, mob)\n",
|
||
"sss.innerHTML"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": ".venv",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": {
|
||
"name": "ipython",
|
||
"version": 3
|
||
},
|
||
"file_extension": ".py",
|
||
"mimetype": "text/x-python",
|
||
"name": "python",
|
||
"nbconvert_exporter": "python",
|
||
"pygments_lexer": "ipython3",
|
||
"version": "3.14.6"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|