Подготовить сайт к запуску
@@ -0,0 +1 @@
|
||||
.webarchive/
|
||||
@@ -0,0 +1,20 @@
|
||||
ProgCode.Ru
|
||||
===========
|
||||
|
||||
Structure:
|
||||
|
||||
- `web/` — Next.js application.
|
||||
- `web/data/articles.json` — article data used by the site.
|
||||
- `web/public/assets/illustrations/` — SVG illustrations and article covers.
|
||||
- `web/public/assets/media/` — raster images used by the site.
|
||||
- `web/scripts/buildDatabase.mjs` — refreshes article data and generated assets from local source files.
|
||||
|
||||
Useful commands:
|
||||
|
||||
```bash
|
||||
cd ~/tmp/progcode/web
|
||||
npm install
|
||||
node scripts/buildDatabase.mjs
|
||||
npm run dev -- --port 3000
|
||||
npm run build
|
||||
```
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
.next
|
||||
node_modules
|
||||
out
|
||||
*.log
|
||||
@@ -0,0 +1 @@
|
||||
registry=https://registry.npmjs.org/
|
||||
@@ -0,0 +1,59 @@
|
||||
import Link from 'next/link';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { formatDate, getArticleBySlug, getArticles } from '../../../lib/articles';
|
||||
|
||||
export function generateStaticParams() {
|
||||
return getArticles().map((article) => ({ slug: article.slug }));
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }) {
|
||||
const { slug } = await params;
|
||||
const article = getArticleBySlug(decodeURIComponent(slug));
|
||||
|
||||
if (!article) {
|
||||
return { title: 'Статья не найдена' };
|
||||
}
|
||||
|
||||
return {
|
||||
title: `${article.title} | ProgCode.Ru`,
|
||||
description: article.excerpt,
|
||||
};
|
||||
}
|
||||
|
||||
export default async function ArticlePage({ params }) {
|
||||
const { slug } = await params;
|
||||
const article = getArticleBySlug(decodeURIComponent(slug));
|
||||
|
||||
if (!article) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<main>
|
||||
<article className="article-page">
|
||||
<nav className="top-nav" aria-label="Навигация">
|
||||
<Link href="/">Все статьи</Link>
|
||||
</nav>
|
||||
|
||||
<header className="article-hero">
|
||||
<div className="article-hero__text">
|
||||
<div className="meta-line">
|
||||
<time dateTime={article.date}>{formatDate(article.date)}</time>
|
||||
<span>{article.author}</span>
|
||||
<span>{article.readingMinutes} мин</span>
|
||||
</div>
|
||||
<h1>{article.title}</h1>
|
||||
<div className="tag-list">
|
||||
{article.categories.map((category) => (
|
||||
<span key={category}>{category}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<img src={article.cover} alt="" />
|
||||
</header>
|
||||
|
||||
<div className="article-content" dangerouslySetInnerHTML={{ __html: article.contentHtml }} />
|
||||
</article>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
:root {
|
||||
--bg: #f3f0e8;
|
||||
--paper: #fffdfa;
|
||||
--ink: #1e252d;
|
||||
--muted: #64707d;
|
||||
--line: #d8d0c2;
|
||||
--accent: #216869;
|
||||
--accent-2: #b4432a;
|
||||
--code: #111827;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg);
|
||||
color: var(--ink);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.site-hero {
|
||||
min-height: 360px;
|
||||
background:
|
||||
linear-gradient(90deg, rgba(20, 24, 28, 0.86), rgba(20, 24, 28, 0.35)),
|
||||
url('/assets/media/cropped-WallpapersMania.nnm_.ru_vol141-087.jpg') center / cover;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.site-hero__inner {
|
||||
width: min(1120px, calc(100% - 40px));
|
||||
margin: 0 auto;
|
||||
padding: 72px 0 46px;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
margin: 0 0 10px;
|
||||
color: var(--accent-2);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.site-hero .eyebrow {
|
||||
color: #f5b08c;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
line-height: 1.15;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.site-hero h1 {
|
||||
font-size: clamp(44px, 8vw, 82px);
|
||||
}
|
||||
|
||||
.site-description {
|
||||
max-width: 680px;
|
||||
margin: 18px 0 0;
|
||||
color: #e5e7eb;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.articles-shell {
|
||||
width: min(1120px, calc(100% - 32px));
|
||||
margin: 34px auto 64px;
|
||||
}
|
||||
|
||||
.articles-header,
|
||||
.top-nav,
|
||||
.meta-line,
|
||||
.tag-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.articles-header {
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.articles-header h2 {
|
||||
font-size: 34px;
|
||||
}
|
||||
|
||||
.articles-header > span,
|
||||
.meta-line {
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.article-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.article-card {
|
||||
min-height: 100%;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: var(--paper);
|
||||
text-decoration: none;
|
||||
transition: transform 160ms ease, border-color 160ms ease, box-shadow 160ms ease;
|
||||
}
|
||||
|
||||
.article-card:hover {
|
||||
border-color: #9f8f7a;
|
||||
box-shadow: 0 12px 30px rgba(39, 33, 25, 0.12);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.article-card > img {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
object-fit: cover;
|
||||
border-bottom: 1px solid var(--line);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.article-card__body {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.article-card h3 {
|
||||
margin-top: 10px;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.article-card p {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.tag-list span {
|
||||
border: 1px solid #c5bba9;
|
||||
border-radius: 999px;
|
||||
padding: 4px 10px;
|
||||
background: #f8f5ee;
|
||||
color: #5c4a37;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.article-page {
|
||||
width: min(1040px, calc(100% - 32px));
|
||||
margin: 0 auto 72px;
|
||||
}
|
||||
|
||||
.top-nav {
|
||||
padding: 22px 0;
|
||||
}
|
||||
|
||||
.top-nav a {
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.article-hero {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(280px, 420px);
|
||||
gap: 28px;
|
||||
align-items: center;
|
||||
padding: 28px 0 34px;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.article-hero h1 {
|
||||
margin: 14px 0 18px;
|
||||
font-size: clamp(34px, 6vw, 58px);
|
||||
}
|
||||
|
||||
.article-hero > img {
|
||||
width: 100%;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.article-content {
|
||||
max-width: 820px;
|
||||
margin: 34px auto 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.article-content h2 {
|
||||
margin: 42px 0 14px;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.article-content h3 {
|
||||
margin: 34px 0 12px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.article-content p,
|
||||
.article-content ul,
|
||||
.article-content ol {
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
|
||||
.article-content a {
|
||||
color: var(--accent);
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
.article-content figure {
|
||||
margin: 28px 0;
|
||||
}
|
||||
|
||||
.article-content figure img {
|
||||
width: 100%;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.article-content figcaption {
|
||||
margin-top: 8px;
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.article-content pre {
|
||||
overflow-x: auto;
|
||||
border-radius: 8px;
|
||||
margin: 26px 0;
|
||||
padding: 18px 20px;
|
||||
background: var(--code);
|
||||
color: #e5e7eb;
|
||||
font-size: 15px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.article-content code {
|
||||
font-family: SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
.article-content :not(pre) > code {
|
||||
border-radius: 4px;
|
||||
padding: 2px 5px;
|
||||
background: #ede6da;
|
||||
color: #663c1f;
|
||||
font-size: 0.92em;
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.site-hero {
|
||||
min-height: 320px;
|
||||
}
|
||||
|
||||
.site-description {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.article-hero {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.article-content {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import './globals.css';
|
||||
import Script from 'next/script';
|
||||
|
||||
export const metadata = {
|
||||
title: 'ProgCode.Ru - блог программиста',
|
||||
description: 'Статьи о PHP, Bitrix, JavaScript, D, Windows и практических задачах разработки.',
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="ru">
|
||||
<body>
|
||||
<Script id="yandex-metrika" strategy="afterInteractive">
|
||||
{`
|
||||
(function(m,e,t,r,i,k,a){
|
||||
m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
||||
m[i].l=1*new Date();
|
||||
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
|
||||
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)
|
||||
})(window, document,'script','https://mc.yandex.ru/metrika/tag.js?id=109268613', 'ym');
|
||||
|
||||
ym(109268613, 'init', {ssr:true, webvisor:true, clickmap:true, ecommerce:"dataLayer", referrer: document.referrer, url: location.href, accurateTrackBounce:true, trackLinks:true});
|
||||
`}
|
||||
</Script>
|
||||
<noscript>
|
||||
<div>
|
||||
<img
|
||||
src="https://mc.yandex.ru/watch/109268613"
|
||||
style={{ position: 'absolute', left: '-9999px' }}
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
</noscript>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import Link from 'next/link';
|
||||
import { formatDate, getArticles } from '../lib/articles';
|
||||
|
||||
export default function HomePage() {
|
||||
const articles = getArticles();
|
||||
|
||||
return (
|
||||
<main>
|
||||
<section className="site-hero">
|
||||
<div className="site-hero__inner">
|
||||
<p className="eyebrow">Личный блог программиста</p>
|
||||
<h1>ProgCode.Ru</h1>
|
||||
<p className="site-description">
|
||||
PHP, Bitrix, JavaScript, D, Windows и практические заметки из ежедневной разработки.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="articles-shell" aria-label="Список статей">
|
||||
<div className="articles-header">
|
||||
<div>
|
||||
<p className="eyebrow">Материалы</p>
|
||||
<h2>Статьи</h2>
|
||||
</div>
|
||||
<span>{articles.length} записей</span>
|
||||
</div>
|
||||
|
||||
<div className="article-grid">
|
||||
{articles.map((article) => (
|
||||
<Link key={article.slug} className="article-card" href={`/articles/${encodeURIComponent(article.slug)}`}>
|
||||
<img src={article.cover} alt="" />
|
||||
<div className="article-card__body">
|
||||
<div className="meta-line">
|
||||
<time dateTime={article.date}>{formatDate(article.date)}</time>
|
||||
<span>{article.readingMinutes} мин</span>
|
||||
</div>
|
||||
<h3>{article.title}</h3>
|
||||
<p>{article.excerpt}</p>
|
||||
<div className="tag-list">
|
||||
{article.categories.map((category) => (
|
||||
<span key={category}>{category}</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import articles from '../data/articles.json';
|
||||
|
||||
export function getArticles() {
|
||||
return articles;
|
||||
}
|
||||
|
||||
export function getArticleBySlug(slug) {
|
||||
return articles.find((article) => article.slug === slug);
|
||||
}
|
||||
|
||||
export function formatDate(date) {
|
||||
return new Intl.DateTimeFormat('ru-RU', {
|
||||
day: '2-digit',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
}).format(new Date(date));
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const nextConfig = {
|
||||
turbopack: {
|
||||
root: dirname(fileURLToPath(import.meta.url)),
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
@@ -0,0 +1,914 @@
|
||||
{
|
||||
"name": "progcode",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "progcode",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"next": "^16.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/colour": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
|
||||
"integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
|
||||
"integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
|
||||
"integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-riscv64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
|
||||
"integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
|
||||
"integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
|
||||
"integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
|
||||
"integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
|
||||
"integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-ppc64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
|
||||
"integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-riscv64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
|
||||
"integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
|
||||
"integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
|
||||
"integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-arm64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
|
||||
"integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
|
||||
"integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
|
||||
"integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.6.tgz",
|
||||
"integrity": "sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.6.tgz",
|
||||
"integrity": "sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.6.tgz",
|
||||
"integrity": "sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.6.tgz",
|
||||
"integrity": "sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.6.tgz",
|
||||
"integrity": "sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.6.tgz",
|
||||
"integrity": "sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.6.tgz",
|
||||
"integrity": "sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.6.tgz",
|
||||
"integrity": "sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.6.tgz",
|
||||
"integrity": "sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.15",
|
||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
|
||||
"integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.10.30",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.30.tgz",
|
||||
"integrity": "sha512-xjOFN16Ha1+Rz4nFYKqHU/LSB+gx/Vi3yQLX7r7sAW+Wa+8hhF2h4pvqTrTMc8+WcDBEunnUurr46Jvv0jk3Vg==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"baseline-browser-mapping": "dist/cli.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001793",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz",
|
||||
"integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/client-only": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.12",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
||||
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "16.2.6",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-16.2.6.tgz",
|
||||
"integrity": "sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@next/env": "16.2.6",
|
||||
"@swc/helpers": "0.5.15",
|
||||
"baseline-browser-mapping": "^2.9.19",
|
||||
"caniuse-lite": "^1.0.30001579",
|
||||
"postcss": "8.4.31",
|
||||
"styled-jsx": "5.1.6"
|
||||
},
|
||||
"bin": {
|
||||
"next": "dist/bin/next"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@next/swc-darwin-arm64": "16.2.6",
|
||||
"@next/swc-darwin-x64": "16.2.6",
|
||||
"@next/swc-linux-arm64-gnu": "16.2.6",
|
||||
"@next/swc-linux-arm64-musl": "16.2.6",
|
||||
"@next/swc-linux-x64-gnu": "16.2.6",
|
||||
"@next/swc-linux-x64-musl": "16.2.6",
|
||||
"@next/swc-win32-arm64-msvc": "16.2.6",
|
||||
"@next/swc-win32-x64-msvc": "16.2.6",
|
||||
"sharp": "^0.34.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"babel-plugin-react-compiler": "*",
|
||||
"react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
|
||||
"react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
|
||||
"sass": "^1.3.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
},
|
||||
"@playwright/test": {
|
||||
"optional": true
|
||||
},
|
||||
"babel-plugin-react-compiler": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.14",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz",
|
||||
"integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.2.6",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz",
|
||||
"integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.2.6",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz",
|
||||
"integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19.2.6"
|
||||
}
|
||||
},
|
||||
"node_modules/scheduler": {
|
||||
"version": "0.27.0",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
|
||||
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz",
|
||||
"integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==",
|
||||
"license": "ISC",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.34.5",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
|
||||
"integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@img/colour": "^1.0.0",
|
||||
"detect-libc": "^2.1.2",
|
||||
"semver": "^7.7.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.34.5",
|
||||
"@img/sharp-darwin-x64": "0.34.5",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-arm": "1.2.4",
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-riscv64": "1.2.4",
|
||||
"@img/sharp-libvips-linux-s390x": "1.2.4",
|
||||
"@img/sharp-libvips-linux-x64": "1.2.4",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.4",
|
||||
"@img/sharp-linux-arm": "0.34.5",
|
||||
"@img/sharp-linux-arm64": "0.34.5",
|
||||
"@img/sharp-linux-ppc64": "0.34.5",
|
||||
"@img/sharp-linux-riscv64": "0.34.5",
|
||||
"@img/sharp-linux-s390x": "0.34.5",
|
||||
"@img/sharp-linux-x64": "0.34.5",
|
||||
"@img/sharp-linuxmusl-arm64": "0.34.5",
|
||||
"@img/sharp-linuxmusl-x64": "0.34.5",
|
||||
"@img/sharp-wasm32": "0.34.5",
|
||||
"@img/sharp-win32-arm64": "0.34.5",
|
||||
"@img/sharp-win32-ia32": "0.34.5",
|
||||
"@img/sharp-win32-x64": "0.34.5"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/styled-jsx": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
|
||||
"integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"client-only": "0.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@babel/core": {
|
||||
"optional": true
|
||||
},
|
||||
"babel-plugin-macros": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "progcode",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "^16.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
"overrides": {
|
||||
"postcss": "^8.5.10"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="860" height="460" viewBox="0 0 860 460" role="img" aria-label="Bitrix API: SKU">
|
||||
<rect width="860" height="460" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="812" height="412" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="812" height="14" rx="7" fill="#1d4ed8"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Bitrix API: SKU</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Создание торгового предложения и привязка к товару</text>
|
||||
|
||||
<rect x="60" y="140" width="740" height="248" rx="14" fill="#111827" stroke="#111827" stroke-width="2"/>
|
||||
<circle cx="92" cy="164" r="6" fill="#ef4444"/><circle cx="114" cy="164" r="6" fill="#f59e0b"/><circle cx="136" cy="164" r="6" fill="#22c55e"/>
|
||||
<text x="84" y="178" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">CModule::IncludeModule("iblock");</text><text x="84" y="206" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">$el = new CIBlockElement;</text><text x="84" y="234" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">$id = $el->Add($fields);</text><text x="84" y="262" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">CPrice::SetBasePrice($id, 990);</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1030" height="720" viewBox="0 0 1030 720" role="img" aria-label="Редактор картинок Bitrix">
|
||||
<rect width="1030" height="720" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="982" height="672" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="982" height="14" rx="7" fill="#1d4ed8"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Редактор картинок Bitrix</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Читаемый интерфейс FileInput</text>
|
||||
<rect x="70" y="150" width="890" height="470" rx="10" fill="#eef3fb" stroke="#bfccdc" stroke-width="2"/><rect x="110" y="200" width="520" height="360" rx="8" fill="#ffffff" stroke="#9aa8b8" stroke-width="2"/><path d="M160 494l132-150 102 110 70-76 116 116z" fill="#93c5fd"/><circle cx="484" cy="286" r="46" fill="#fde68a"/><rect x="668" y="200" width="238" height="56" rx="8" fill="#fff" stroke="#d1d8e0" stroke-width="2"/><text x="690" y="236" font-size="20" fill="#1f2933">Обрезать</text><rect x="668" y="278" width="238" height="56" rx="8" fill="#fff" stroke="#d1d8e0" stroke-width="2"/><text x="690" y="314" font-size="20" fill="#1f2933">Повернуть</text><rect x="668" y="356" width="238" height="56" rx="8" fill="#1d4ed8" stroke="#1d4ed8" stroke-width="2"/><text x="710" y="392" font-size="20" font-weight="700" fill="#fff">Сохранить</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="860" height="420" viewBox="0 0 860 420" role="img" aria-label="Bitrix photo editor">
|
||||
<rect width="860" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="812" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="812" height="14" rx="7" fill="#1d4ed8"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Bitrix photo editor</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Встроенный FileInput для загрузки и редактирования картинки</text>
|
||||
<rect x="64" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="84" y="229" font-size="17" font-weight="700" fill="#1f2933">FileInput</text><text x="84" y="258" font-size="13" fill="#52606d">create</text><path d="M196 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M232 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="242" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="262" y="229" font-size="17" font-weight="700" fill="#1f2933">upload</text><text x="262" y="258" font-size="13" fill="#52606d">image</text><path d="M374 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M410 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="420" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="440" y="229" font-size="17" font-weight="700" fill="#1f2933">edit</text><text x="440" y="258" font-size="13" fill="#52606d">crop</text><path d="M552 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M588 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="598" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="618" y="229" font-size="17" font-weight="700" fill="#1f2933">save</text><text x="618" y="258" font-size="13" fill="#52606d">file ID</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="860" height="460" viewBox="0 0 860 460" role="img" aria-label="Bitrix API: translit">
|
||||
<rect width="860" height="460" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="812" height="412" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="812" height="14" rx="7" fill="#0f766e"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Bitrix API: translit</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Символьный код из имени элемента</text>
|
||||
|
||||
<rect x="60" y="140" width="740" height="248" rx="14" fill="#111827" stroke="#111827" stroke-width="2"/>
|
||||
<circle cx="92" cy="164" r="6" fill="#ef4444"/><circle cx="114" cy="164" r="6" fill="#f59e0b"/><circle cx="136" cy="164" r="6" fill="#22c55e"/>
|
||||
<text x="84" y="178" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">$code = CUtil::translit($name, "ru", [</text><text x="84" y="206" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace"> "replace_space" => "-",</text><text x="84" y="234" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace"> "replace_other" => "-",</text><text x="84" y="262" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">]);</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="480" viewBox="0 0 900 480" role="img" aria-label="Планировщик против рекламного запуска">
|
||||
<rect width="900" height="480" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="852" height="432" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="852" height="14" rx="7" fill="#2f6fed"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Планировщик против рекламного запуска</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Схема поиска вредного задания</text>
|
||||
<rect x="86" y="168" width="728" height="178" rx="12" fill="#eef3fb" stroke="#bfccdc" stroke-width="2"/><text x="128" y="230" font-size="38" font-weight="700" fill="#1f2933" font-family="monospace">taskschd.msc</text><text x="128" y="286" font-size="28" fill="#b42318" font-family="monospace">firefox.exe + URL</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 991 B |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="860" height="420" viewBox="0 0 860 420" role="img" aria-label="NewCTFE">
|
||||
<rect width="860" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="812" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="812" height="14" rx="7" fill="#7c3aed"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">NewCTFE</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Вычисления во время компиляции без лишней магии</text>
|
||||
<rect x="64" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="84" y="229" font-size="17" font-weight="700" fill="#1f2933">AST</text><text x="84" y="258" font-size="13" fill="#52606d">input</text><path d="M196 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M232 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="242" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="262" y="229" font-size="17" font-weight="700" fill="#1f2933">CTFE VM</text><text x="262" y="258" font-size="13" fill="#52606d">evaluate</text><path d="M374 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M410 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="420" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="440" y="229" font-size="17" font-weight="700" fill="#1f2933">Result</text><text x="440" y="258" font-size="13" fill="#52606d">constant</text><path d="M552 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M588 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="598" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="618" y="229" font-size="17" font-weight="700" fill="#1f2933">Binary</text><text x="618" y="258" font-size="13" fill="#52606d">ready</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="860" height="420" viewBox="0 0 860 420" role="img" aria-label="D GC under the hood">
|
||||
<rect width="860" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="812" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="812" height="14" rx="7" fill="#0f766e"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">D GC under the hood</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Пулы, страницы, маркировка и очистка</text>
|
||||
<rect x="64" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="84" y="229" font-size="17" font-weight="700" fill="#1f2933">Pool</text><text x="84" y="258" font-size="13" fill="#52606d">memory</text><path d="M196 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M232 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="242" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="262" y="229" font-size="17" font-weight="700" fill="#1f2933">Mark</text><text x="262" y="258" font-size="13" fill="#52606d">scan</text><path d="M374 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M410 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="420" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="440" y="229" font-size="17" font-weight="700" fill="#1f2933">Sweep</text><text x="440" y="258" font-size="13" fill="#52606d">free</text><path d="M552 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M588 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="598" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="618" y="229" font-size="17" font-weight="700" fill="#1f2933">Reuse</text><text x="618" y="258" font-size="13" fill="#52606d">alloc</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="860" height="420" viewBox="0 0 860 420" role="img" aria-label="DMD x64 под Windows">
|
||||
<rect width="860" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="812" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="812" height="14" rx="7" fill="#7c2d12"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">DMD x64 под Windows</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Не смешиваем OMF и COFF библиотеки</text>
|
||||
<rect x="64" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="84" y="229" font-size="17" font-weight="700" fill="#1f2933">D source</text><text x="84" y="258" font-size="13" fill="#52606d">main.d</text><path d="M196 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M232 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="242" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="262" y="229" font-size="17" font-weight="700" fill="#1f2933">dmd -m64</text><text x="262" y="258" font-size="13" fill="#52606d">compiler</text><path d="M374 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M410 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="420" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="440" y="229" font-size="17" font-weight="700" fill="#1f2933">COFF libs</text><text x="440" y="258" font-size="13" fill="#52606d">x64</text><path d="M552 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M588 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="598" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="618" y="229" font-size="17" font-weight="700" fill="#1f2933">EXE</text><text x="618" y="258" font-size="13" fill="#52606d">win64</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="860" height="420" viewBox="0 0 860 420" role="img" aria-label="Firefox timeout">
|
||||
<rect width="860" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="812" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="812" height="14" rx="7" fill="#ea580c"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Firefox timeout</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Когда браузеру нужно ждать дольше</text>
|
||||
<rect x="80" y="170" width="700" height="80" rx="12" fill="#fff7ed" stroke="#fed7aa" stroke-width="2"/><text x="112" y="222" font-size="28" font-weight="700" fill="#9a3412" font-family="monospace">network.http.response.timeout = 600</text><rect x="80" y="286" width="520" height="26" rx="13" fill="#fed7aa" stroke="#fed7aa" stroke-width="2"/><rect x="80" y="286" width="390" height="26" rx="13" fill="#ea580c" stroke="#ea580c" stroke-width="2"/><text x="80" y="352" font-size="18" fill="#52606d">Для административных операций, а не для маскировки медленного сайта.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="520" viewBox="0 0 900 520" role="img" aria-label="GC D: большие объекты">
|
||||
<rect width="900" height="520" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="852" height="472" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="852" height="14" rx="7" fill="#c2410c"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">GC D: большие объекты</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Метаданные растут вместе с диапазоном страниц</text>
|
||||
|
||||
<rect x="86" y="166" width="728" height="92" rx="10" fill="#fff7ed" stroke="#fdba74" stroke-width="2"/>
|
||||
<text x="112" y="222" font-size="25" font-weight="700" fill="#9a3412" font-family="monospace">object > 100 MB</text>
|
||||
<rect x="86" y="310" width="728" height="58" rx="8" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/>
|
||||
<rect x="108" y="323" width="24" height="32" rx="4" fill="#fb923c"/><rect x="146" y="323" width="24" height="32" rx="4" fill="#fb923c"/><rect x="184" y="323" width="24" height="32" rx="4" fill="#fb923c"/><rect x="222" y="323" width="24" height="32" rx="4" fill="#fb923c"/><rect x="260" y="323" width="24" height="32" rx="4" fill="#fb923c"/><rect x="298" y="323" width="24" height="32" rx="4" fill="#fb923c"/><rect x="336" y="323" width="24" height="32" rx="4" fill="#fb923c"/><rect x="374" y="323" width="24" height="32" rx="4" fill="#fb923c"/><rect x="412" y="323" width="24" height="32" rx="4" fill="#fb923c"/><rect x="450" y="323" width="24" height="32" rx="4" fill="#fb923c"/><rect x="488" y="323" width="24" height="32" rx="4" fill="#fb923c"/><rect x="526" y="323" width="24" height="32" rx="4" fill="#cbd5e1"/><rect x="564" y="323" width="24" height="32" rx="4" fill="#cbd5e1"/><rect x="602" y="323" width="24" height="32" rx="4" fill="#cbd5e1"/><rect x="640" y="323" width="24" height="32" rx="4" fill="#cbd5e1"/><rect x="678" y="323" width="24" height="32" rx="4" fill="#cbd5e1"/><rect x="716" y="323" width="24" height="32" rx="4" fill="#cbd5e1"/><rect x="754" y="323" width="24" height="32" rx="4" fill="#cbd5e1"/>
|
||||
<text x="86" y="410" font-size="17" fill="#52606d">Один большой объект может оставить заметный хвост таблиц и свободных страниц.</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="860" height="460" viewBox="0 0 860 460" role="img" aria-label="jQuery в Webpack">
|
||||
<rect width="860" height="460" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="812" height="412" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="812" height="14" rx="7" fill="#2563eb"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">jQuery в Webpack</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Один экземпляр библиотеки для модулей и старых плагинов</text>
|
||||
|
||||
<rect x="60" y="140" width="740" height="248" rx="14" fill="#111827" stroke="#111827" stroke-width="2"/>
|
||||
<circle cx="92" cy="164" r="6" fill="#ef4444"/><circle cx="114" cy="164" r="6" fill="#f59e0b"/><circle cx="136" cy="164" r="6" fill="#22c55e"/>
|
||||
<text x="84" y="178" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">import $ from "jquery";</text><text x="84" y="206" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">window.$ = window.jQuery = $;</text><text x="84" y="234" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace"></text><text x="84" y="262" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">new webpack.ProvidePlugin({</text><text x="84" y="290" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace"> $: "jquery",</text><text x="84" y="318" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace"> jQuery: "jquery"</text><text x="84" y="346" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">});</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="860" height="420" viewBox="0 0 860 420" role="img" aria-label="PHP SSL CA bundle">
|
||||
<rect width="860" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="812" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="812" height="14" rx="7" fill="#0f766e"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">PHP SSL CA bundle</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Ошибка лечится сертификатами, а не отключением проверки</text>
|
||||
<rect x="64" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="84" y="229" font-size="17" font-weight="700" fill="#1f2933">PHP</text><text x="84" y="258" font-size="13" fill="#52606d">request</text><path d="M196 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M232 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="242" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="262" y="229" font-size="17" font-weight="700" fill="#1f2933">OpenSSL</text><text x="262" y="258" font-size="13" fill="#52606d">verify</text><path d="M374 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M410 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="420" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="440" y="229" font-size="17" font-weight="700" fill="#1f2933">CA file</text><text x="440" y="258" font-size="13" fill="#52606d">cacert.pem</text><path d="M552 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M588 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="598" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="618" y="229" font-size="17" font-weight="700" fill="#1f2933">HTTPS</text><text x="618" y="258" font-size="13" fill="#52606d">ok</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="860" height="460" viewBox="0 0 860 460" role="img" aria-label="preg_match_all на D">
|
||||
<rect width="860" height="460" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="812" height="412" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="812" height="14" rx="7" fill="#be123c"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">preg_match_all на D</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Регулярные выражения, группы и позиции совпадений</text>
|
||||
|
||||
<rect x="60" y="140" width="740" height="248" rx="14" fill="#111827" stroke="#111827" stroke-width="2"/>
|
||||
<circle cx="92" cy="164" r="6" fill="#ef4444"/><circle cx="114" cy="164" r="6" fill="#f59e0b"/><circle cx="136" cy="164" r="6" fill="#22c55e"/>
|
||||
<text x="84" y="178" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">auto r = regex(r"(\\w+)=(\\d+)");</text><text x="84" y="206" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">auto m = matchAll("a=1 b=22", r);</text><text x="84" y="234" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace"></text><text x="84" y="262" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">foreach (hit; m) {</text><text x="84" y="290" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace"> writeln(hit.captures);</text><text x="84" y="318" font-size="17" fill="#d7e3f5" font-family="SFMono-Regular, Menlo, Consolas, monospace">}</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="980" height="520" viewBox="0 0 980 520" role="img" aria-label="Планировщик заданий: действие">
|
||||
<rect width="980" height="520" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="932" height="472" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="932" height="14" rx="7" fill="#2f6fed"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Планировщик заданий: действие</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">В аргументах браузера виден лишний URL</text>
|
||||
|
||||
<rect x="70" y="148" width="840" height="300" rx="8" fill="#eef3fb" stroke="#bfccdc" stroke-width="2"/>
|
||||
<text x="96" y="186" font-size="22" font-weight="700" fill="#1f2933">Библиотека планировщика заданий</text>
|
||||
|
||||
<rect x="96" y="212" width="300" height="192" rx="6" fill="#fff" stroke="#d1d8e0" stroke-width="2"/>
|
||||
<text x="118" y="248" font-size="17" fill="#1f2933">Update Service</text>
|
||||
<text x="118" y="282" font-size="17" fill="#1f2933">Browser Updater</text>
|
||||
<text x="118" y="316" font-size="17" font-weight="700" fill="#b42318">Mozilla Firefox Task</text>
|
||||
<rect x="430" y="212" width="420" height="192" rx="6" fill="#fff" stroke="#d1d8e0" stroke-width="2"/>
|
||||
<text x="456" y="250" font-size="16" fill="#52606d">Программа:</text>
|
||||
<text x="456" y="282" font-size="20" fill="#1f2933" font-family="monospace">firefox.exe</text>
|
||||
<text x="456" y="326" font-size="16" fill="#52606d">Аргументы:</text>
|
||||
<text x="456" y="358" font-size="20" fill="#b42318" font-family="monospace">http://example-bad-site.test</text>
|
||||
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="980" height="520" viewBox="0 0 980 520" role="img" aria-label="Запуск планировщика заданий">
|
||||
<rect width="980" height="520" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="932" height="472" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="932" height="14" rx="7" fill="#2f6fed"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Запуск планировщика заданий</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Win + R, затем taskschd.msc</text>
|
||||
|
||||
<rect x="70" y="148" width="840" height="300" rx="8" fill="#eef3fb" stroke="#bfccdc" stroke-width="2"/>
|
||||
<text x="96" y="186" font-size="22" font-weight="700" fill="#1f2933">Выполнить</text>
|
||||
|
||||
<text x="110" y="245" font-size="18" fill="#52606d">Открыть:</text>
|
||||
<rect x="110" y="268" width="540" height="54" rx="6" fill="#fff" stroke="#9aa8b8" stroke-width="2"/>
|
||||
<text x="132" y="303" font-size="24" fill="#1f2933" font-family="monospace">taskschd.msc</text>
|
||||
<rect x="670" y="268" width="128" height="54" rx="6" fill="#2f6fed" stroke="#2f6fed" stroke-width="2"/>
|
||||
<text x="704" y="303" font-size="22" font-weight="700" fill="#fff">OK</text>
|
||||
<text x="110" y="372" font-size="17" fill="#52606d">Так открывается системный инструмент, где видны все периодические запуски.</text>
|
||||
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="860" height="420" viewBox="0 0 860 420" role="img" aria-label="Tilix + D">
|
||||
<rect width="860" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="812" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="812" height="14" rx="7" fill="#7c3aed"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Tilix + D</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Desktop-приложение на GtkD</text>
|
||||
<rect x="64" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="84" y="229" font-size="17" font-weight="700" fill="#1f2933">GtkD</text><text x="84" y="258" font-size="13" fill="#52606d">UI</text><path d="M196 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M232 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="242" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="262" y="229" font-size="17" font-weight="700" fill="#1f2933">D</text><text x="262" y="258" font-size="13" fill="#52606d">logic</text><path d="M374 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M410 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="420" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="440" y="229" font-size="17" font-weight="700" fill="#1f2933">VTE</text><text x="440" y="258" font-size="13" fill="#52606d">terminal</text><path d="M552 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M588 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="598" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="618" y="229" font-size="17" font-weight="700" fill="#1f2933">GNOME</text><text x="618" y="258" font-size="13" fill="#52606d">HIG</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,24 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1040" height="600" viewBox="0 0 1040 600" role="img" aria-label="Tilix">
|
||||
<rect width="1040" height="600" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="992" height="552" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="992" height="14" rx="7" fill="#7c3aed"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Tilix</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Тайлинговый терминал с боковой панелью сессий</text>
|
||||
|
||||
<rect x="66" y="130" width="908" height="390" rx="12" fill="#1f2933" stroke="#111827" stroke-width="2"/>
|
||||
<rect x="66" y="130" width="908" height="44" rx="12" fill="#343b46"/>
|
||||
<text x="100" y="159" font-size="16" fill="#d7e3f5" font-family="monospace">project: ~/src/tilix</text>
|
||||
<rect x="88" y="198" width="170" height="286" rx="8" fill="#111827" stroke="#334155" stroke-width="2"/>
|
||||
<text x="112" y="232" font-size="15" fill="#94a3b8" font-family="monospace">sessions</text>
|
||||
<text x="112" y="272" font-size="18" fill="#f8fafc" font-family="monospace">build</text>
|
||||
<text x="112" y="310" font-size="18" fill="#f8fafc" font-family="monospace">ssh prod</text>
|
||||
<text x="112" y="348" font-size="18" fill="#f8fafc" font-family="monospace">logs</text>
|
||||
<rect x="286" y="198" width="308" height="132" rx="8" fill="#0f172a" stroke="#475569" stroke-width="2"/>
|
||||
<rect x="616" y="198" width="308" height="132" rx="8" fill="#0f172a" stroke="#475569" stroke-width="2"/>
|
||||
<rect x="286" y="352" width="638" height="132" rx="8" fill="#0f172a" stroke="#475569" stroke-width="2"/>
|
||||
<text x="316" y="238" font-size="19" fill="#a7f3d0" font-family="monospace">$ dub build</text>
|
||||
<text x="646" y="238" font-size="19" fill="#bfdbfe" font-family="monospace">$ journalctl -f</text>
|
||||
<text x="316" y="394" font-size="19" fill="#fde68a" font-family="monospace">$ grep -R GtkD source/</text>
|
||||
<text x="316" y="430" font-size="18" fill="#e5e7eb" font-family="monospace">ready</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="420" viewBox="0 0 760 420" role="img" aria-label="Короткий API">
|
||||
<rect width="760" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="712" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="712" height="14" rx="7" fill="#2563eb"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Короткий API</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Минимум кода для типовых веб-задач</text>
|
||||
<circle cx="90" cy="160" r="14" fill="#2563eb"/><path d="M83 160l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="168" font-size="20" fill="#263241">маршруты рядом с логикой</text><circle cx="90" cy="218" r="14" fill="#2563eb"/><path d="M83 218l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="226" font-size="20" fill="#263241">простые настройки сервера</text><circle cx="90" cy="276" r="14" fill="#2563eb"/><path d="M83 276l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="284" font-size="20" fill="#263241">DUB-пакеты</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="420" viewBox="0 0 760 420" role="img" aria-label="Асинхронность">
|
||||
<rect width="760" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="712" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="712" height="14" rx="7" fill="#059669"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Асинхронность</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Задачи выглядят синхронно, I/O остаётся неблокирующим</text>
|
||||
<circle cx="90" cy="160" r="14" fill="#059669"/><path d="M83 160l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="168" font-size="20" fill="#263241">fiber на запрос</text><circle cx="90" cy="218" r="14" fill="#059669"/><path d="M83 218l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="226" font-size="20" fill="#263241">ожидание без блокировки</text><circle cx="90" cy="276" r="14" fill="#059669"/><path d="M83 276l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="284" font-size="20" fill="#263241">простая модель ошибок</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="860" height="420" viewBox="0 0 860 420" role="img" aria-label="vibe.d">
|
||||
<rect width="860" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="812" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="812" height="14" rx="7" fill="#059669"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">vibe.d</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Асинхронный веб-стек на языке D</text>
|
||||
<rect x="64" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="84" y="229" font-size="17" font-weight="700" fill="#1f2933">HTTP</text><text x="84" y="258" font-size="13" fill="#52606d">server</text><path d="M196 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M232 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="242" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="262" y="229" font-size="17" font-weight="700" fill="#1f2933">Fiber</text><text x="262" y="258" font-size="13" fill="#52606d">async I/O</text><path d="M374 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M410 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="420" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="440" y="229" font-size="17" font-weight="700" fill="#1f2933">Mongo/Redis</text><text x="440" y="258" font-size="13" fill="#52606d">drivers</text><path d="M552 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M588 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><rect x="598" y="190" width="130" height="96" rx="12" fill="#f8fafc" stroke="#d1d8e0" stroke-width="2"/><text x="618" y="229" font-size="17" font-weight="700" fill="#1f2933">Diet</text><text x="618" y="258" font-size="13" fill="#52606d">views</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="420" viewBox="0 0 760 420" role="img" aria-label="Базы данных">
|
||||
<rect width="760" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="712" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="712" height="14" rx="7" fill="#0f766e"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Базы данных</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">MongoDB и Redis доступны из коробки</text>
|
||||
<circle cx="90" cy="160" r="14" fill="#0f766e"/><path d="M83 160l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="168" font-size="20" fill="#263241">асинхронные драйверы</text><circle cx="90" cy="218" r="14" fill="#0f766e"/><path d="M83 218l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="226" font-size="20" fill="#263241">простые запросы</text><circle cx="90" cy="276" r="14" fill="#0f766e"/><path d="M83 276l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="284" font-size="20" fill="#263241">расширение через DUB</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="420" viewBox="0 0 760 420" role="img" aria-label="Diet templates">
|
||||
<rect width="760" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="712" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="712" height="14" rx="7" fill="#0f172a"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Diet templates</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">HTML без лишнего синтаксического шума</text>
|
||||
<circle cx="90" cy="160" r="14" fill="#0f172a"/><path d="M83 160l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="168" font-size="20" fill="#263241">наследник Pug/Haml</text><circle cx="90" cy="218" r="14" fill="#0f172a"/><path d="M83 218l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="226" font-size="20" fill="#263241">динамические вставки</text><circle cx="90" cy="276" r="14" fill="#0f172a"/><path d="M83 276l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="284" font-size="20" fill="#263241">читаемые view</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="420" viewBox="0 0 760 420" role="img" aria-label="Язык D">
|
||||
<rect width="760" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="712" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="712" height="14" rx="7" fill="#b91c1c"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Язык D</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">Скорость нативного кода и выразительность высокого уровня</text>
|
||||
<circle cx="90" cy="160" r="14" fill="#b91c1c"/><path d="M83 160l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="168" font-size="20" fill="#263241">типы и шаблоны</text><circle cx="90" cy="218" r="14" fill="#b91c1c"/><path d="M83 218l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="226" font-size="20" fill="#263241">быстрая компиляция</text><circle cx="90" cy="276" r="14" fill="#b91c1c"/><path d="M83 276l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="284" font-size="20" fill="#263241">удобная метапрограмма</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="420" viewBox="0 0 760 420" role="img" aria-label="Неблокирующий I/O">
|
||||
<rect width="760" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="712" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="712" height="14" rx="7" fill="#9333ea"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Неблокирующий I/O</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">libevent и системные API вместо ожидания потока</text>
|
||||
<circle cx="90" cy="160" r="14" fill="#9333ea"/><path d="M83 160l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="168" font-size="20" fill="#263241">сетевые операции</text><circle cx="90" cy="218" r="14" fill="#9333ea"/><path d="M83 218l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="226" font-size="20" fill="#263241">файловые операции</text><circle cx="90" cy="276" r="14" fill="#9333ea"/><path d="M83 276l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="284" font-size="20" fill="#263241">меньше простаивания</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="760" height="420" viewBox="0 0 760 420" role="img" aria-label="Веб-инструменты">
|
||||
<rect width="760" height="420" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="712" height="372" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="712" height="14" rx="7" fill="#dc2626"/>
|
||||
<text x="56" y="82" font-size="30" font-weight="700" fill="#1f2933">Веб-инструменты</text>
|
||||
<text x="56" y="116" font-size="16" fill="#52606d">HTTP, WebSocket, сессии и шаблоны</text>
|
||||
<circle cx="90" cy="160" r="14" fill="#dc2626"/><path d="M83 160l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="168" font-size="20" fill="#263241">статические файлы</text><circle cx="90" cy="218" r="14" fill="#dc2626"/><path d="M83 218l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="226" font-size="20" fill="#263241">JSON/BSON</text><circle cx="90" cy="276" r="14" fill="#dc2626"/><path d="M83 276l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><text x="124" y="284" font-size="20" fill="#263241">SMTP и криптография</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 139 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1,583 @@
|
||||
import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const webRoot = join(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const sourceRoot = join(webRoot, '..', '.webarchive');
|
||||
const illustrationsDir = join(webRoot, 'public', 'assets', 'illustrations');
|
||||
const mediaDir = join(webRoot, 'public', 'assets', 'media');
|
||||
const dataDir = join(webRoot, 'data');
|
||||
|
||||
const rawArticles = JSON.parse(await readFile(join(sourceRoot, 'extracted-articles.json'), 'utf8'));
|
||||
|
||||
const categories = {
|
||||
'вирус-самопроизвольный-запуск-брауз': ['Windows', 'Вирусы'],
|
||||
'использование-jquery-в-webpack': ['JavaScript'],
|
||||
'компиляция-64-x-разрядных-программ-на-dmd-по': ['DLang', 'DMD', 'Windows'],
|
||||
'новый-движок-ctfe': ['DLang', 'Compile-time'],
|
||||
'особенности-vibe-d': ['DLang', 'Vibe.d'],
|
||||
'ошибка-php-ssl-certificate-error-unable-to-get-local-issuer-certificate': ['PHP', 'SSL'],
|
||||
'о-tilix-и-d-интервью-с-геральдом-нанном': ['DLang', 'Интервью'],
|
||||
'пишем-аналог-функции-php-preg_match_all-на-языке-прог': ['DLang', 'Регулярные выражения'],
|
||||
'bitrix-api-создание-добавление-торгового-пре': ['Bitrix', 'PHP'],
|
||||
'bitrix-api-функция-для-генерации-кода-элемент': ['Bitrix', 'PHP'],
|
||||
'bitrix-api-add-foto-editor': ['Bitrix', 'PHP'],
|
||||
'dconf-2017-под-капотом-мусорщика-ди-дмитрий-ол': ['DLang', 'GC'],
|
||||
'firefox-увеличение-ожидания-загрузки-timeout-стр': ['Firefox', 'Настройки'],
|
||||
};
|
||||
|
||||
const covers = {
|
||||
'вирус-самопроизвольный-запуск-брауз': '/assets/illustrations/cover-virus.svg',
|
||||
'использование-jquery-в-webpack': '/assets/illustrations/jquery-webpack.svg',
|
||||
'компиляция-64-x-разрядных-программ-на-dmd-по': '/assets/illustrations/dmd-win64.svg',
|
||||
'новый-движок-ctfe': '/assets/illustrations/ctfe-engine.svg',
|
||||
'особенности-vibe-d': '/assets/illustrations/vibe-cover.svg',
|
||||
'ошибка-php-ssl-certificate-error-unable-to-get-local-issuer-certificate': '/assets/illustrations/php-ssl-cacert.svg',
|
||||
'о-tilix-и-d-интервью-с-геральдом-нанном': '/assets/illustrations/tilix-cover.svg',
|
||||
'пишем-аналог-функции-php-preg_match_all-на-языке-прог': '/assets/illustrations/preg-match-all-d.svg',
|
||||
'bitrix-api-создание-добавление-торгового-пре': '/assets/illustrations/bitrix-offer-api.svg',
|
||||
'bitrix-api-функция-для-генерации-кода-элемент': '/assets/illustrations/bitrix-translit-api.svg',
|
||||
'bitrix-api-add-foto-editor': '/assets/illustrations/bitrix-photo-editor.svg',
|
||||
'dconf-2017-под-капотом-мусорщика-ди-дмитрий-ол': '/assets/illustrations/dconf-gc-cover.svg',
|
||||
'firefox-увеличение-ожидания-загрузки-timeout-стр': '/assets/illustrations/firefox-timeout.svg',
|
||||
};
|
||||
|
||||
const imageMap = [
|
||||
['2017-05-13_13-39-44', '/assets/illustrations/taskschd-run.svg', 'Окно запуска taskschd.msc'],
|
||||
['2017-05-13_13-41-17', '/assets/illustrations/taskschd-action.svg', 'Подозрительное задание в планировщике Windows'],
|
||||
['/2017/05/1.png', '/assets/illustrations/vibe-async.svg', 'Асинхронная модель vibe.d'],
|
||||
['/2017/05/2.png', '/assets/illustrations/vibe-api.svg', 'Короткий API vibe.d'],
|
||||
['/2017/05/3.png', '/assets/illustrations/vibe-web.svg', 'Веб-стек vibe.d'],
|
||||
['/2017/05/4.png', '/assets/illustrations/vibe-db.svg', 'Драйверы баз данных vibe.d'],
|
||||
['/2017/05/5.png', '/assets/illustrations/vibe-io.svg', 'Неблокирующий ввод-вывод'],
|
||||
['/2017/05/6.png', '/assets/illustrations/vibe-dlang.svg', 'Язык D как основа vibe.d'],
|
||||
['/2017/05/7.png', '/assets/illustrations/vibe-diet.svg', 'Шаблоны Diet'],
|
||||
['tilix-screenshot-2', '/assets/illustrations/tilix-screenshot.svg', 'Боковая панель Tilix в действии'],
|
||||
['/2017/06/1-300x225', '/assets/illustrations/gc-small-pools.svg', 'Пулы малых объектов в GC D'],
|
||||
['/2017/06/1.jpg', '/assets/illustrations/gc-small-pools.svg', 'Пулы малых объектов в GC D'],
|
||||
['/2017/06/2-200x300', '/assets/illustrations/gc-large-pool.svg', 'Пул больших объектов в GC D'],
|
||||
['/2017/06/2.jpg', '/assets/illustrations/gc-large-pool.svg', 'Пул больших объектов в GC D'],
|
||||
['bitrix-foto-editor-2', '/assets/illustrations/bitrix-photo-editor-ui.svg', 'Редактор картинок Bitrix'],
|
||||
];
|
||||
|
||||
const additions = {
|
||||
'вирус-самопроизвольный-запуск-брауз': `
|
||||
<h2>Проверяем, что проблема решена</h2>
|
||||
<p>После удаления задания перезагрузите компьютер и оставьте систему включенной на тот промежуток времени, через который раньше открывался браузер. Если окно больше не появляется, значит источник найден правильно. Если запуск повторяется, проверьте не только библиотеку планировщика, но и вкладку автозагрузки, ключи Run в реестре и расширения браузера.</p>
|
||||
<ul>
|
||||
<li>В планировщике смотрите не только имя задания, но и действие: путь к браузеру и URL в аргументах.</li>
|
||||
<li>Удаляйте только понятные вредоносные задания. Системные задачи Microsoft лучше сначала отключить, а не стирать.</li>
|
||||
<li>После очистки обновите антивирус и выполните полную проверку, потому что задание могло быть лишь следом установщика.</li>
|
||||
</ul>
|
||||
<p>В итоге рецепт простой: найти периодический запуск, проверить действие задания, удалить его и убедиться, что источник не восстанавливается после перезагрузки.</p>`,
|
||||
'использование-jquery-в-webpack': `
|
||||
<h2>Итоговая схема подключения</h2>
|
||||
<p>Для старого проекта jQuery в Webpack лучше подключать явно: установить пакет, импортировать его в точке входа и, если плагинам нужна глобальная переменная, добавить ProvidePlugin. Тогда один и тот же экземпляр библиотеки будет использоваться и вашим кодом, и плагинами.</p>
|
||||
<pre><code>const webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({
|
||||
$: 'jquery',
|
||||
jQuery: 'jquery',
|
||||
'window.jQuery': 'jquery',
|
||||
}),
|
||||
],
|
||||
};</code></pre>
|
||||
<p>Если плагин всё равно не видит jQuery, проверьте порядок импортов и отсутствие второй копии библиотеки в bundle. На этом обычно все проблемы заканчиваются.</p>`,
|
||||
'компиляция-64-x-разрядных-программ-на-dmd-по': `
|
||||
<h2>Короткая памятка</h2>
|
||||
<p>Чтобы не возвращаться к этой настройке каждый раз, держите отдельный build-скрипт для 64-битной сборки и явно указывайте архитектуру, компилятор и путь к библиотекам. Главная мысль статьи: не смешивать 32-битные OMF-библиотеки и 64-битную сборку.</p>
|
||||
<pre><code>dmd -m64 main.d -ofapp.exe
|
||||
dub build --arch=x86_64</code></pre>
|
||||
<p>Если линковщик ругается на формат библиотек, сначала проверьте разрядность каждой внешней зависимости. В большинстве случаев ошибка не в D-коде, а в том, что рядом оказалась библиотека другого формата.</p>`,
|
||||
'новый-движок-ctfe': `
|
||||
<h2>Как применять выводы на практике</h2>
|
||||
<p>CTFE полезен там, где результат можно посчитать один раз во время компиляции: таблицы констант, парсинг небольших DSL, генерация однотипного кода. Но не стоит превращать компиляцию в полноценный рантайм. Чем проще входные данные и чем меньше побочных эффектов, тем легче будет сопровождать проект.</p>
|
||||
<p>Практическое правило такое: если вычисление зависит только от литералов и конфигурации сборки, переносите его в CTFE. Если нужен ввод-вывод, сеть или состояние окружения, оставьте это обычному коду.</p>`,
|
||||
'особенности-vibe-d': `
|
||||
<h2>Когда выбирать vibe.d</h2>
|
||||
<p>vibe.d хорошо подходит для небольших и средних веб-сервисов на D, где важны неблокирующий ввод-вывод, простая маршрутизация и единая экосистема DUB. Начать можно с минимального HTTP-сервера, затем добавить Diet-шаблоны, MongoDB или Redis и вынести повторяющиеся части в отдельные модули.</p>
|
||||
<pre><code>shared static this()
|
||||
{
|
||||
auto settings = new HTTPServerSettings;
|
||||
settings.port = 8080;
|
||||
listenHTTP(settings, &handleRequest);
|
||||
}</code></pre>
|
||||
<p>Если проект требует большого количества готовых интеграций, заранее проверьте наличие пакетов в DUB. Если же нужна компактная серверная часть на D, vibe.d остаётся прямым и понятным выбором.</p>`,
|
||||
'ошибка-php-ssl-certificate-error-unable-to-get-local-issuer-certificate': `
|
||||
<h2>Финальная проверка настройки</h2>
|
||||
<p>После подключения файла сертификатов перезапустите веб-сервер или PHP-FPM и выполните тестовый HTTPS-запрос из того же окружения, где возникала ошибка. Важно проверять не браузер, а именно PHP: CLI и веб-сервер могут использовать разные php.ini.</p>
|
||||
<pre><code>php -i | grep cafile
|
||||
php -r "var_dump(file_get_contents('https://example.com') !== false);"</code></pre>
|
||||
<p>Не отключайте проверку SSL через CURLOPT_SSL_VERIFYPEER = false как постоянное решение. Это скрывает проблему и делает соединение уязвимым. Правильный путь — актуальный CA bundle и явно указанная директива openssl.cafile.</p>`,
|
||||
'о-tilix-и-d-интервью-с-геральдом-нанном': `
|
||||
<h2>Что вынести из интервью</h2>
|
||||
<p>Главный практический вывод прост: D может быть не только системным языком, но и удобным языком для прикладных desktop-инструментов. Tilix показывает, что связка D, GtkD и аккуратного следования HIG способна дать зрелое приложение, которым пользуются каждый день.</p>
|
||||
<p>Если хочется повторить такой путь, начинайте не с большого терминала, а с маленькой GTK-утилиты: окно, настройки, несколько действий, сборка через DUB. Так быстрее станет понятно, насколько привычки из Java, Delphi или PHP мешают или помогают писать на D.</p>`,
|
||||
'пишем-аналог-функции-php-preg_match_all-на-языке-прог': `
|
||||
<h2>Как довести класс до рабочего состояния</h2>
|
||||
<p>После реализации флагов обязательно добавьте тесты на три случая: нет совпадений, одно совпадение с группами и несколько совпадений с позициями. Именно на позициях чаще всего появляются ошибки, потому что PHP возвращает смещения в исходной строке, а не в подстроке после очередного поиска.</p>
|
||||
<pre><code>assert(matchAll(r"(\w+)=(\d+)", "a=1 b=22").length == 2);
|
||||
assert(matchAll(r"z+", "abc").empty);</code></pre>
|
||||
<p>Дальше можно расширять API постепенно: сначала PREG_PATTERN_ORDER, затем PREG_SET_ORDER, и только после этого режим с OFFSET_CAPTURE.</p>`,
|
||||
'bitrix-api-создание-добавление-торгового-пре': `
|
||||
<h2>Что проверить после создания предложения</h2>
|
||||
<p>После добавления торгового предложения проверьте три связи: привязку к товару, цены и остатки. В Bitrix предложение может успешно создаться, но не появиться в публичной части, если не заполнены обязательные свойства SKU или не обновлены индексы.</p>
|
||||
<ul>
|
||||
<li>Убедитесь, что установлен CML2_LINK или соответствующее свойство связи.</li>
|
||||
<li>После записи цены вызовите обновление элемента и очистите кеш каталога.</li>
|
||||
<li>Если используются склады, отдельно сохраните количество через API складского учёта.</li>
|
||||
</ul>
|
||||
<p>Такой порядок закрывает типичную проблему: элемент есть в админке, но покупатель его не видит.</p>`,
|
||||
'bitrix-api-функция-для-генерации-кода-элемент': `
|
||||
<h2>Как использовать функцию безопасно</h2>
|
||||
<p>Транслитерация имени удобна, но код элемента должен оставаться уникальным. Поэтому после генерации проверяйте существующие символьные коды в инфоблоке и при совпадении добавляйте числовой суффикс.</p>
|
||||
<pre><code>telefon-samsung
|
||||
telefon-samsung-2
|
||||
telefon-samsung-3</code></pre>
|
||||
<p>Такой простой контроль избавляет от случайной перезаписи URL и от ситуаций, когда два товара получают один и тот же человекочитаемый адрес.</p>`,
|
||||
'bitrix-api-add-foto-editor': `
|
||||
<h2>Завершение интеграции</h2>
|
||||
<p>После вывода компонента важно не забыть обработать результат формы: сохранить ID файла, проверить тип загруженного файла и права пользователя. Сам редактор решает задачу интерфейса, но безопасность и привязка к сущности остаются на стороне вашего кода.</p>
|
||||
<p>Если редактор не открывается, проверьте подключение модулей main и fileman, права на загрузку и наличие административных JS-расширений на странице. После этого компонент работает стабильно и может использоваться не только в админке, но и в собственных формах.</p>`,
|
||||
'dconf-2017-под-капотом-мусорщика-ди-дмитрий-ол': `
|
||||
<h2>Практический вывод</h2>
|
||||
<p>Статья важна не только для разработчиков компилятора. Она показывает, почему аллокатор и сборщик мусора нельзя оценивать одной фразой «GC медленный». Нужно смотреть на классы размеров, поиск пулов, стоимость маркировки и то, сколько работы выполняется во время остановки мира.</p>
|
||||
<p>Для прикладного D-кода вывод такой: держите горячие циклы предсказуемыми, не создавайте лишние временные объекты в tight-loop и измеряйте паузы. Если участок действительно критичен, D позволяет локально отключить GC или заменить аллокации заранее подготовленными буферами.</p>`,
|
||||
'firefox-увеличение-ожидания-загрузки-timeout-стр': `
|
||||
<h2>Когда лучше не увеличивать timeout</h2>
|
||||
<p>Увеличение ожидания помогает при разовых административных операциях, но не должно маскировать медленный сайт. Если обычная страница требует минуты, правильнее вынести задачу в фон: очередь, cron, прогресс-бар и отдельная проверка статуса.</p>
|
||||
<p>Для phpMyAdmin, импорта и парсинга больших файлов увеличение timeout допустимо. Для публичного пользовательского сценария лучше исправлять серверную часть, иначе браузер станет ждать дольше, но пользователь всё равно получит плохой интерфейс.</p>`,
|
||||
};
|
||||
|
||||
const articleAdditions = {
|
||||
...additions,
|
||||
'вирус-самопроизвольный-запуск-брауз': `
|
||||
<h2>Проверяем, что проблема решена</h2>
|
||||
<p>После удаления задания перезагрузите компьютер и оставьте систему включённой на тот промежуток времени, через который раньше открывался браузер. Если окно больше не появляется, значит источник найден правильно. Если запуск повторяется, проверяем не только библиотеку планировщика, но и автозагрузку, ключи Run в реестре и расширения браузера.</p>
|
||||
<p>В планировщике заданий важно смотреть не только название задания. Название злоумышленник может сделать вполне безобидным: Update Service, Browser Check, Windows Helper. Смысл находится в действии. Если в действии указан путь к браузеру, а после него стоит адрес сайта, то перед нами как раз тот самый случай.</p>
|
||||
<ul>
|
||||
<li>Откройте свойства подозрительного задания и посмотрите вкладку «Действия».</li>
|
||||
<li>Проверьте путь к программе: firefox.exe, chrome.exe, browser.exe или cmd.exe с последующим запуском браузера.</li>
|
||||
<li>Проверьте аргументы: если там указан URL, рекламный домен или короткая ссылка, задание можно отключать.</li>
|
||||
<li>Сначала отключите задание, перезагрузите компьютер и проверьте поведение. После проверки его уже можно удалить.</li>
|
||||
</ul>
|
||||
<p>Если задача появляется снова, значит в системе остался установщик или служба, которая её восстанавливает. В таком случае дополнительно смотрим «Приложения и возможности», папки автозагрузки пользователя, ключи <em>HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run</em> и <em>HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Run</em>. После очистки нужно обновить антивирусные базы и выполнить полную проверку.</p>
|
||||
<p>Итог простой: в этом случае лечится не браузер, а причина его запуска. Находим периодическое задание, проверяем действие, отключаем, убеждаемся, что оно не восстановилось, и только после этого удаляем. Так проблема закрывается полностью, а не до следующей перезагрузки.</p>`,
|
||||
'использование-jquery-в-webpack': `
|
||||
<h2>Итоговая схема подключения</h2>
|
||||
<p>Для старого проекта jQuery в Webpack лучше подключать явно: установить пакет, импортировать его в точке входа и отдельно решить вопрос с глобальными переменными. ProvidePlugin удобен, когда в модулях встречаются свободные идентификаторы <em>$</em> или <em>jQuery</em>. Но если старый плагин лезет именно в <em>window.jQuery</em>, одного ProvidePlugin может быть мало — нужно положить jQuery в <em>window</em> самостоятельно.</p>
|
||||
<pre><code>import $ from 'jquery';
|
||||
|
||||
window.$ = $;
|
||||
window.jQuery = $;</code></pre>
|
||||
<p>После этого можно добавить ProvidePlugin, чтобы не писать импорт в каждом файле:</p>
|
||||
<pre><code>const webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
new webpack.ProvidePlugin({
|
||||
$: 'jquery',
|
||||
jQuery: 'jquery',
|
||||
}),
|
||||
],
|
||||
};</code></pre>
|
||||
<p>Порядок подключения имеет значение. Сначала в entry-файле импортируем jQuery и кладём его в <em>window</em>, потом импортируем старые плагины, которым нужен глобальный объект. Если плагин подключается через <em>require</em>, делаем это после установки <em>window.jQuery</em>.</p>
|
||||
<pre><code>import './jquery-global';
|
||||
import 'inputmask/dist/jquery.inputmask';
|
||||
import './app';</code></pre>
|
||||
<p>Проверка простая: в браузерной консоли должны существовать <em>window.$</em> и <em>window.jQuery</em>, а в собранном bundle не должно быть двух разных копий jQuery. Если проект новый, лучше не тащить jQuery без необходимости. Если проект старый и плагины уже написаны под jQuery, такая схема делает зависимость явной и предсказуемой.</p>`,
|
||||
'компиляция-64-x-разрядных-программ-на-dmd-по': `
|
||||
<h2>Короткая памятка для современных и старых сборок</h2>
|
||||
<p>Главная мысль остаётся той же: нельзя смешивать 32-битные OMF-библиотеки и 64-битную сборку. В старых установках DMD под Windows это часто упиралось в ручную настройку <em>sc.ini</em>, Windows SDK и Visual Studio linker. В более новых установках официальный установщик DMD обычно делает большую часть этой настройки сам, а для нормальной работы достаточно иметь Visual Studio Build Tools или установленную Visual Studio с C++ tooling.</p>
|
||||
<p>Для простой 64-битной сборки используем явную архитектуру:</p>
|
||||
<pre><code>dmd -m64 main.d -of=app.exe
|
||||
dub build --arch=x86_64</code></pre>
|
||||
<p>Если используется старая 32-битная сборка, смотрим, какой формат объектных файлов нужен проекту. Старый OMF и MS-COFF несовместимы между собой. Для современных библиотек Windows чаще нужен MS-COFF, а для 64-битной сборки это фактически обычный путь.</p>
|
||||
<ul>
|
||||
<li>Проверьте, что внешние <em>.lib</em> файлы собраны под ту же архитектуру.</li>
|
||||
<li>Запускайте сборку из Developer Command Prompt или x64 Native Tools Command Prompt, если компоновщик не находится.</li>
|
||||
<li>Если DMD не видит linker, проверьте переменные окружения и секцию <em>Environment64</em> в <em>sc.ini</em>.</li>
|
||||
<li>Если проект собирается через DUB, фиксируйте архитектуру в команде или конфигурации сборки.</li>
|
||||
</ul>
|
||||
<p>Типичная ошибка выглядит так: код компилируется, а линковка падает на внешней библиотеке. В большинстве случаев виноват не D-код, а то, что рядом лежит библиотека другого формата или другой разрядности. Сначала проверяем toolchain и зависимости, и только потом ищем ошибку в исходниках.</p>`,
|
||||
'новый-движок-ctfe': `
|
||||
<h2>Как применять выводы на практике</h2>
|
||||
<p>CTFE полезен там, где результат можно посчитать один раз во время компиляции: таблицы констант, парсинг небольших DSL, генерация однотипного кода, предварительная подготовка строк и проверка инвариантов. Но не стоит превращать компиляцию в полноценный runtime. Чем проще входные данные и чем меньше побочных эффектов, тем легче будет сопровождать проект.</p>
|
||||
<pre><code>enum table = buildLookupTable();
|
||||
|
||||
static assert(table.length == 256);</code></pre>
|
||||
<p>Хороший кандидат для CTFE — функция, которая зависит только от литералов, типов и конфигурации сборки. Плохой кандидат — код, которому нужен ввод-вывод, сеть, текущее время, состояние окружения или большая внешняя база данных. Такой код лучше оставить в runtime или вынести в отдельный генератор, который запускается до сборки.</p>
|
||||
<p>Для контроля качества CTFE-кода полезно держать рядом <em>static assert</em>. Он сразу показывает, что вычисление действительно произошло на этапе компиляции и вернуло ожидаемый результат. Если compile-time функция стала слишком сложной, её стоит разбить на маленькие чистые функции: так компилятору проще, и человеку проще понять, что происходит.</p>
|
||||
<p>Практический вывод такой: CTFE в D — мощный инструмент, но сила его не в магии, а в переносе предсказуемых вычислений из runtime в compile-time. Используем его для констант, генерации и проверок. Не используем его как замену обычной программе.</p>`,
|
||||
'особенности-vibe-d': `
|
||||
<h2>Когда выбирать vibe.d</h2>
|
||||
<p>vibe.d хорошо подходит для небольших и средних веб-сервисов на D, где важны неблокирующий ввод-вывод, простая маршрутизация и единая экосистема DUB. Начать можно с минимального HTTP-сервера, затем добавить Diet-шаблоны, MongoDB или Redis и вынести повторяющиеся части в отдельные модули.</p>
|
||||
<pre><code>shared static this()
|
||||
{
|
||||
auto settings = new HTTPServerSettings;
|
||||
settings.port = 8080;
|
||||
listenHTTP(settings, &handleRequest);
|
||||
}</code></pre>
|
||||
<p>При проектировании сервиса главное не забывать, что удобный синтаксис не отменяет асинхронную модель. Долгие CPU-задачи нельзя выполнять прямо в обработчике запроса, иначе они будут мешать обработке ввода-вывода. Для таких задач лучше использовать отдельные worker-потоки, очередь или заранее подготовленные данные.</p>
|
||||
<ul>
|
||||
<li>HTTP и WebSocket оставляем в event loop vibe.d.</li>
|
||||
<li>Тяжёлые вычисления выносим из обработчика запроса.</li>
|
||||
<li>Доступ к базе оборачиваем в небольшой слой, чтобы не размазывать запросы по маршрутам.</li>
|
||||
<li>Diet-шаблоны держим простыми: шаблон должен отображать данные, а не выполнять бизнес-логику.</li>
|
||||
</ul>
|
||||
<p>Если проект требует большого количества готовых интеграций, заранее проверьте наличие пакетов в DUB и состояние нужных драйверов. Если же нужна компактная серверная часть на D, vibe.d остаётся прямым и понятным выбором: один язык, быстрый нативный код, нормальная маршрутизация, шаблоны и асинхронный ввод-вывод.</p>`,
|
||||
'ошибка-php-ssl-certificate-error-unable-to-get-local-issuer-certificate': `
|
||||
<h2>Финальная проверка настройки</h2>
|
||||
<p>После подключения файла сертификатов перезапустите веб-сервер или PHP-FPM и выполните тестовый HTTPS-запрос из того же окружения, где возникала ошибка. Важно проверять не браузер, а именно PHP: CLI и веб-сервер могут использовать разные <em>php.ini</em>.</p>
|
||||
<pre><code>php --ini
|
||||
php -i | grep -E "curl.cainfo|openssl.cafile"
|
||||
php -r "var_dump(file_get_contents('https://example.com') !== false);"</code></pre>
|
||||
<p>Для cURL указывается <em>curl.cainfo</em>, для потоков OpenSSL — <em>openssl.cafile</em>. На Windows путь лучше писать полностью и без относительных директорий. Если используется не глобальный <em>php.ini</em>, а отдельная настройка клиента, можно передать CA bundle прямо в cURL:</p>
|
||||
<pre><code>curl_setopt($ch, CURLOPT_CAINFO, 'C:\\php\\extras\\ssl\\cacert.pem');</code></pre>
|
||||
<p>Не отключайте проверку SSL через <em>CURLOPT_SSL_VERIFYPEER = false</em> как постоянное решение. Это скрывает проблему и делает соединение уязвимым. Такая настройка допустима только для короткой диагностики в локальной среде, и после проверки её нужно вернуть обратно.</p>
|
||||
<p>Если CA bundle указан верно, но ошибка осталась, проверьте цепочку сертификатов на стороне удалённого сервера. Иногда браузер открывает сайт, потому что умеет достраивать цепочку, а PHP/cURL получает неполную цепочку и честно падает. В таком случае исправлять нужно сертификаты сервера, а не PHP-код.</p>
|
||||
<p>Правильный итог: актуальный <em>cacert.pem</em>, явно указанные <em>curl.cainfo</em> и <em>openssl.cafile</em>, перезапуск PHP и тест именно из того окружения, где работает приложение.</p>`,
|
||||
'о-tilix-и-d-интервью-с-геральдом-нанном': `
|
||||
<h2>Что вынести из интервью</h2>
|
||||
<p>Главный практический вывод прост: D может быть не только системным языком, но и удобным языком для прикладных desktop-инструментов. Tilix показывает, что связка D, GtkD и аккуратного следования GNOME HIG способна дать зрелое приложение, которым пользуются каждый день.</p>
|
||||
<p>В интервью хорошо видно, что выбор языка был не религиозным, а практическим. Автору не хотелось писать GUI на C или C++, Python оказался неудобен для крупного статически проверяемого приложения, а D дал знакомую после Java модель, нативную скорость и нормальные GTK-привязки. Это важный критерий выбора технологии: не «самый модный язык», а язык, на котором конкретный автор может быстро и спокойно делать продукт.</p>
|
||||
<p>Если хочется повторить такой путь, начинать стоит не с большого терминала, а с маленькой GTK-утилиты: окно, настройки, несколько действий, сборка через DUB, упаковка и обновления. На таком проекте сразу проявятся реальные вопросы: работа с GtkD, структура проекта, взаимодействие с C-библиотеками, обработка событий и паузы GC.</p>
|
||||
<ul>
|
||||
<li>Для GUI-кода держите бизнес-логику отдельно от виджетов.</li>
|
||||
<li>Не бойтесь GC, но измеряйте горячие участки и большие циклы.</li>
|
||||
<li>Если нужен C API, сначала сделайте маленький прототип вызова.</li>
|
||||
<li>Сначала исправляйте ошибки, потом добавляйте функции — именно так Tilix сохранил управляемый backlog.</li>
|
||||
</ul>
|
||||
<p>Именно поэтому интервью полезно не только поклонникам D. Оно показывает нормальный инженерный путь: выбрать задачу, подобрать инструмент, сделать небольшую работающую версию, а затем постепенно доводить приложение до зрелого состояния.</p>`,
|
||||
'пишем-аналог-функции-php-preg_match_all-на-языке-прог': `
|
||||
<h2>Как довести класс до рабочего состояния</h2>
|
||||
<p>После реализации флагов обязательно добавьте тесты на три случая: нет совпадений, одно совпадение с группами и несколько совпадений с позициями. Именно на позициях чаще всего появляются ошибки, потому что PHP возвращает смещения в исходной строке, а не в подстроке после очередного поиска.</p>
|
||||
<pre><code>assert(matchAll(r"(\w+)=(\d+)", "a=1 b=22").length == 2);
|
||||
assert(matchAll(r"z+", "abc").empty);</code></pre>
|
||||
<p>В D стоит явно решить, какой формат результата нужен. PHP поддерживает несколько режимов: <em>PREG_PATTERN_ORDER</em>, <em>PREG_SET_ORDER</em> и вариант с <em>PREG_OFFSET_CAPTURE</em>. Если пытаться сделать всё сразу, код быстро станет запутанным. Лучше сначала реализовать простой режим, затем режим группировки по совпадениям, и только после этого добавить позиции.</p>
|
||||
<pre><code>struct MatchPart
|
||||
{
|
||||
string text;
|
||||
ptrdiff_t offset;
|
||||
}
|
||||
|
||||
alias MatchSet = MatchPart[][];</code></pre>
|
||||
<p>Отдельно проверьте Unicode. Если шаблон и строка содержат кириллицу, важно понимать, что именно возвращается в offset: позиция в байтах или позиция в символах. PHP для <em>PREG_OFFSET_CAPTURE</em> возвращает байтовое смещение. Если в D вы хотите повторить поведение PHP, нужно документировать именно байтовый offset, иначе результаты будут отличаться.</p>
|
||||
<p>Финальный класс должен иметь маленькую поверхность API: метод без offset, метод с offset и понятный enum для порядка результата. Тогда аналог <em>preg_match_all</em> будет не просто копией PHP-функции, а удобным D-инструментом, который предсказуемо работает в тестах.</p>`,
|
||||
'bitrix-api-создание-добавление-торгового-пре': `
|
||||
<h2>Что проверить после создания предложения</h2>
|
||||
<p>После добавления торгового предложения проверьте три связи: привязку к товару, цены и остатки. В Bitrix предложение может успешно создаться, но не появиться в публичной части, если не заполнены обязательные свойства SKU, не сохранён товарный каталог или не обновлены кеши.</p>
|
||||
<ul>
|
||||
<li>Убедитесь, что модуль <em>iblock</em> подключён перед созданием элемента.</li>
|
||||
<li>Если задаются цена и складской остаток, подключите модуль <em>catalog</em>.</li>
|
||||
<li>Проверьте свойство связи с товаром, чаще всего это <em>CML2_LINK</em> или настроенное в конкретном каталоге свойство SKU.</li>
|
||||
<li>После записи цены и остатков очистите кеш компонента каталога.</li>
|
||||
</ul>
|
||||
<p>Минимальная последовательность такая: создать элемент торгового предложения в инфоблоке SKU, записать связь с товаром, сохранить параметры товара, установить цену, установить количество. Если хотя бы один шаг пропущен, предложение может быть видно в админке, но не попадёт в публичный каталог.</p>
|
||||
<pre><code>CModule::IncludeModule('iblock');
|
||||
CModule::IncludeModule('catalog');
|
||||
|
||||
$offerId = $el->Add($fields);
|
||||
CCatalogProduct::Add([
|
||||
'ID' => $offerId,
|
||||
'QUANTITY' => 10,
|
||||
]);
|
||||
CPrice::SetBasePrice($offerId, 990, 'RUB');</code></pre>
|
||||
<p>На боевом проекте обязательно логируйте результат <em>Add</em> и текст ошибки <em>LAST_ERROR</em>. Bitrix часто молча возвращает <em>false</em>, а настоящая причина находится именно там: обязательное поле, неправильный инфоблок, неверный код свойства или недостаточные права.</p>`,
|
||||
'bitrix-api-функция-для-генерации-кода-элемент': `
|
||||
<h2>Как использовать функцию безопасно</h2>
|
||||
<p>Транслитерация имени удобна, но код элемента должен оставаться уникальным. Поэтому после генерации проверяйте существующие символьные коды в инфоблоке и при совпадении добавляйте числовой суффикс. Иначе два товара с похожим названием могут получить один URL.</p>
|
||||
<pre><code>telefon-samsung
|
||||
telefon-samsung-2
|
||||
telefon-samsung-3</code></pre>
|
||||
<p>Также стоит сразу нормализовать результат: привести к нижнему регистру, заменить пробелы на дефис, убрать повторяющиеся дефисы и обрезать дефис в начале или конце строки. Это мелочь, но именно такие мелочи потом портят адреса страниц.</p>
|
||||
<pre><code>$code = CUtil::translit($name, 'ru', [
|
||||
'replace_space' => '-',
|
||||
'replace_other' => '-',
|
||||
'change_case' => 'L',
|
||||
]);
|
||||
|
||||
$code = trim(preg_replace('/-+/', '-', $code), '-');</code></pre>
|
||||
<p>Проверку уникальности лучше делать в том же инфоблоке, где будет создан элемент. Если сайт многоязычный или есть разделы с одинаковыми товарами, заранее решите правило: уникальность на весь инфоблок или только внутри раздела. Для SEO обычно проще и надёжнее уникальность на весь инфоблок.</p>
|
||||
<p>В итоге функция должна не просто транслитерировать строку, а возвращать готовый символьный код: чистый, нижнего регистра, без мусора и без совпадений с уже существующими элементами.</p>`,
|
||||
'bitrix-api-add-foto-editor': `
|
||||
<h2>Завершение интеграции</h2>
|
||||
<p>После вывода компонента важно не забыть обработать результат формы: сохранить ID файла, проверить тип загруженного файла и права пользователя. Сам редактор решает задачу интерфейса, но безопасность и привязка к сущности остаются на стороне вашего кода.</p>
|
||||
<p>В параметрах компонента отдельно проверьте <em>allowUpload</em>, <em>medialib</em>, <em>fileDialog</em>, <em>cloud</em>, <em>delete</em>, <em>edit</em> и <em>maxCount</em>. Не все параметры будут иметь эффект в любой установке Bitrix: часть зависит от подключённых модулей, прав пользователя и контекста страницы.</p>
|
||||
<pre><code>$fileId = (int)$_POST['picture'];
|
||||
|
||||
if ($fileId > 0) {
|
||||
$file = CFile::GetFileArray($fileId);
|
||||
if ($file && str_starts_with($file['CONTENT_TYPE'], 'image/')) {
|
||||
// сохраняем ID картинки в своей сущности
|
||||
}
|
||||
}</code></pre>
|
||||
<p>Если редактор не открывается, проверьте подключение модулей <em>main</em> и <em>fileman</em>, права на загрузку, административные JS-расширения и наличие сессии пользователя. В публичной части сайта проблема часто оказывается не в <em>FileInput</em>, а в том, что на странице не подключены нужные скрипты Bitrix.</p>
|
||||
<p>Рабочая схема такая: выводим FileInput, разрешаем только картинки, ограничиваем количество, после отправки формы валидируем ID файла и сохраняем его в свою сущность. Тогда встроенный редактор становится нормальной частью формы, а не просто красивой кнопкой загрузки.</p>`,
|
||||
'dconf-2017-под-капотом-мусорщика-ди-дмитрий-ол': `
|
||||
<h2>Практический вывод</h2>
|
||||
<p>Статья важна не только для разработчиков компилятора. Она показывает, почему аллокатор и сборщик мусора нельзя оценивать одной фразой «GC медленный». Нужно смотреть на классы размеров, поиск пулов, стоимость маркировки и то, сколько работы выполняется во время остановки мира.</p>
|
||||
<p>Для прикладного D-кода вывод такой: держите горячие циклы предсказуемыми, не создавайте лишние временные объекты в tight-loop и измеряйте паузы. Если участок действительно критичен, D позволяет локально управлять GC и заменить поток мелких аллокаций заранее подготовленными буферами.</p>
|
||||
<pre><code>import core.memory : GC;
|
||||
|
||||
GC.disable();
|
||||
scope(exit) GC.enable();
|
||||
|
||||
// горячий участок без лишних временных объектов</code></pre>
|
||||
<p>Отключать GC нужно аккуратно. Это не волшебная оптимизация, а договор: пока сборщик выключен, программа не должна бесконтрольно накапливать мусор. Поэтому такой приём подходит для короткого измеримого участка, а не для всего приложения.</p>
|
||||
<ul>
|
||||
<li>Сначала измеряем: время, количество аллокаций, паузы.</li>
|
||||
<li>Потом убираем лишние временные объекты и повторные выделения.</li>
|
||||
<li>Только затем используем ручное управление GC, если оно действительно помогает.</li>
|
||||
<li>После оптимизации оставляем комментарий, почему здесь нельзя писать «просто как обычно».</li>
|
||||
</ul>
|
||||
<p>Главный смысл доклада в том, что производительность памяти — это архитектура. Если в программе много мелких объектов, непредсказуемых ссылок и больших пауз, проблема редко решается одной настройкой. Нужно менять модель данных, жизненный цикл объектов и место, где выделяется память.</p>`,
|
||||
'firefox-увеличение-ожидания-загрузки-timeout-стр': `
|
||||
<h2>Когда лучше не увеличивать timeout</h2>
|
||||
<p>Увеличение ожидания помогает при разовых административных операциях, но не должно маскировать медленный сайт. Если обычная страница требует минуты, правильнее вынести задачу в фон: очередь, cron, прогресс-бар и отдельная проверка статуса.</p>
|
||||
<p>Важно не путать разные таймауты. Ожидание HTTP-ответа — это одно, долго выполняющийся JavaScript на странице — другое, а таймауты прокси или веб-сервера — третье. Если Firefox ждёт дольше, но nginx, Apache, PHP-FPM или upstream уже оборвал соединение, настройка браузера не поможет.</p>
|
||||
<ul>
|
||||
<li>Для долгого ответа сервера проверяйте HTTP timeout в браузере и серверные таймауты.</li>
|
||||
<li>Для долгого JavaScript смотрите предупреждения о зависшем скрипте и настройки выполнения JS.</li>
|
||||
<li>Для импорта больших файлов лучше использовать фоновую задачу и страницу прогресса.</li>
|
||||
<li>Для phpMyAdmin и разовых административных операций увеличение ожидания допустимо.</li>
|
||||
</ul>
|
||||
<p>Если параметра в <em>about:config</em> нет, его можно создать вручную только тогда, когда вы понимаете, какая версия Firefox и какой именно timeout вам нужен. После завершения работы лучше вернуть настройку к обычному значению, чтобы браузер не висел слишком долго на реально недоступных сайтах.</p>
|
||||
<p>Итог: увеличивать timeout можно как временный инструмент администратора. Для пользовательского сценария правильнее исправлять серверную часть, потому что долгий белый экран остаётся плохим интерфейсом даже тогда, когда браузер согласен ждать.</p>`,
|
||||
};
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value)
|
||||
.replaceAll('&', '&')
|
||||
.replaceAll('<', '<')
|
||||
.replaceAll('>', '>')
|
||||
.replaceAll('"', '"');
|
||||
}
|
||||
|
||||
function decodeHtml(value = '') {
|
||||
const named = { amp: '&', apos: "'", gt: '>', hellip: '...', laquo: '«', lt: '<', mdash: '—', nbsp: ' ', quot: '"', raquo: '»' };
|
||||
return value
|
||||
.replace(/&#x([0-9a-f]+);/gi, (_, hex) => String.fromCodePoint(Number.parseInt(hex, 16)))
|
||||
.replace(/&#(\d+);/g, (_, dec) => String.fromCodePoint(Number.parseInt(dec, 10)))
|
||||
.replace(/&([a-z]+);/gi, (_, name) => named[name] ?? `&${name};`);
|
||||
}
|
||||
|
||||
function stripTags(value = '') {
|
||||
return decodeHtml(value.replace(/<[^>]+>/g, ' ')).replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
|
||||
function normalizeContent(html) {
|
||||
let content = html
|
||||
.replace(/<span id="more-\d+"><\/span>/g, '')
|
||||
.replace(/<script[\s\S]*?<\/script>/gi, '')
|
||||
.replace(/<style[\s\S]*?<\/style>/gi, '');
|
||||
|
||||
content = content.replace(/<pre[^>]*>([\s\S]*?)<\/pre>/gi, (_, code) => {
|
||||
const text = decodeHtml(code.replace(/<[^>]+>/g, ''));
|
||||
return `<pre><code>${escapeHtml(text.trim())}</code></pre>`;
|
||||
});
|
||||
|
||||
content = content.replace(/<img\b[^>]*>/gi, (tag) => {
|
||||
const src = decodeHtml(tag.match(/\bsrc=["']([^"']+)["']/i)?.[1] ?? '');
|
||||
const found = imageMap.find(([needle]) => src.includes(needle));
|
||||
if (!found) return '';
|
||||
const [, imageSrc, defaultAlt] = found;
|
||||
const originalAlt = decodeHtml(tag.match(/\balt=["']([^"']*)["']/i)?.[1] ?? '');
|
||||
const alt = originalAlt || defaultAlt;
|
||||
return `<figure class="article-media"><img src="${imageSrc}" alt="${escapeHtml(alt)}" /><figcaption>${escapeHtml(defaultAlt)}</figcaption></figure>`;
|
||||
});
|
||||
|
||||
content = content
|
||||
.replace(/\s+(srcset|sizes|width|height|class)=["'][^"']*["']/gi, '')
|
||||
.replace(/<p>\s*<\/p>/g, '')
|
||||
.replace(/\n{3,}/g, '\n\n')
|
||||
.trim();
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
function text(x, y, body, options = {}) {
|
||||
const attrs = [
|
||||
`x="${x}"`,
|
||||
`y="${y}"`,
|
||||
options.size ? `font-size="${options.size}"` : '',
|
||||
options.weight ? `font-weight="${options.weight}"` : '',
|
||||
options.fill ? `fill="${options.fill}"` : '',
|
||||
options.family ? `font-family="${options.family}"` : '',
|
||||
].filter(Boolean).join(' ');
|
||||
return `<text ${attrs}>${escapeHtml(body)}</text>`;
|
||||
}
|
||||
|
||||
function rect(x, y, width, height, fill, stroke = '#263241', radius = 10) {
|
||||
return `<rect x="${x}" y="${y}" width="${width}" height="${height}" rx="${radius}" fill="${fill}" stroke="${stroke}" stroke-width="2"/>`;
|
||||
}
|
||||
|
||||
function baseSvg(width, height, title, subtitle, accent, body) {
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img" aria-label="${escapeHtml(title)}">
|
||||
<rect width="${width}" height="${height}" fill="#f7f4ed"/>
|
||||
<rect x="24" y="24" width="${width - 48}" height="${height - 48}" rx="18" fill="#ffffff" stroke="#242424" stroke-width="2"/>
|
||||
<rect x="24" y="24" width="${width - 48}" height="14" rx="7" fill="${accent}"/>
|
||||
${text(56, 82, title, { size: 30, weight: 700, fill: '#1f2933' })}
|
||||
${subtitle ? text(56, 116, subtitle, { size: 16, fill: '#52606d' }) : ''}
|
||||
${body}
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
function diagramAsset(title, subtitle, accent, labels) {
|
||||
const items = labels.map((item, index) => {
|
||||
const x = 64 + index * 178;
|
||||
const arrow = index < labels.length - 1 ? `<path d="M${x + 132} 248h44" stroke="#687789" stroke-width="4" stroke-linecap="round"/><path d="M${x + 168} 238l16 10-16 10" fill="none" stroke="#687789" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>` : '';
|
||||
return `${rect(x, 190, 130, 96, '#f8fafc', '#d1d8e0', 12)}${text(x + 20, 229, item[0], { size: 17, weight: 700, fill: '#1f2933' })}${text(x + 20, 258, item[1], { size: 13, fill: '#52606d' })}${arrow}`;
|
||||
}).join('');
|
||||
return baseSvg(860, 420, title, subtitle, accent, items);
|
||||
}
|
||||
|
||||
function codeAsset(title, subtitle, accent, lines) {
|
||||
const code = lines.map((line, index) => text(84, 178 + index * 28, line, { size: 17, fill: '#d7e3f5', family: 'SFMono-Regular, Menlo, Consolas, monospace' })).join('');
|
||||
return baseSvg(860, 460, title, subtitle, accent, `
|
||||
${rect(60, 140, 740, 248, '#111827', '#111827', 14)}
|
||||
<circle cx="92" cy="164" r="6" fill="#ef4444"/><circle cx="114" cy="164" r="6" fill="#f59e0b"/><circle cx="136" cy="164" r="6" fill="#22c55e"/>
|
||||
${code}
|
||||
`);
|
||||
}
|
||||
|
||||
function featureCard(title, subtitle, accent, rows) {
|
||||
const body = rows.map((row, index) => {
|
||||
const y = 168 + index * 58;
|
||||
return `<circle cx="90" cy="${y - 8}" r="14" fill="${accent}"/><path d="M83 ${y - 8}l5 6 11-14" fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>${text(124, y, row, { size: 20, fill: '#263241' })}`;
|
||||
}).join('');
|
||||
return baseSvg(760, 420, title, subtitle, accent, body);
|
||||
}
|
||||
|
||||
function taskScheduler(kind) {
|
||||
const isAction = kind === 'action';
|
||||
return baseSvg(980, 520, isAction ? 'Планировщик заданий: действие' : 'Запуск планировщика заданий', isAction ? 'В аргументах браузера виден лишний URL' : 'Win + R, затем taskschd.msc', '#2f6fed', `
|
||||
${rect(70, 148, 840, 300, '#eef3fb', '#bfccdc', 8)}
|
||||
${text(96, 186, isAction ? 'Библиотека планировщика заданий' : 'Выполнить', { size: 22, weight: 700, fill: '#1f2933' })}
|
||||
${isAction ? `
|
||||
${rect(96, 212, 300, 192, '#fff', '#d1d8e0', 6)}
|
||||
${text(118, 248, 'Update Service', { size: 17, fill: '#1f2933' })}
|
||||
${text(118, 282, 'Browser Updater', { size: 17, fill: '#1f2933' })}
|
||||
${text(118, 316, 'Mozilla Firefox Task', { size: 17, fill: '#b42318', weight: 700 })}
|
||||
${rect(430, 212, 420, 192, '#fff', '#d1d8e0', 6)}
|
||||
${text(456, 250, 'Программа:', { size: 16, fill: '#52606d' })}
|
||||
${text(456, 282, 'firefox.exe', { size: 20, fill: '#1f2933', family: 'monospace' })}
|
||||
${text(456, 326, 'Аргументы:', { size: 16, fill: '#52606d' })}
|
||||
${text(456, 358, 'http://example-bad-site.test', { size: 20, fill: '#b42318', family: 'monospace' })}
|
||||
` : `
|
||||
${text(110, 245, 'Открыть:', { size: 18, fill: '#52606d' })}
|
||||
${rect(110, 268, 540, 54, '#fff', '#9aa8b8', 6)}
|
||||
${text(132, 303, 'taskschd.msc', { size: 24, fill: '#1f2933', family: 'monospace' })}
|
||||
${rect(670, 268, 128, 54, '#2f6fed', '#2f6fed', 6)}
|
||||
${text(704, 303, 'OK', { size: 22, fill: '#fff', weight: 700 })}
|
||||
${text(110, 372, 'Так открывается системный инструмент, где видны все периодические запуски.', { size: 17, fill: '#52606d' })}
|
||||
`}
|
||||
`);
|
||||
}
|
||||
|
||||
function tilixScreenshot() {
|
||||
return baseSvg(1040, 600, 'Tilix', 'Тайлинговый терминал с боковой панелью сессий', '#7c3aed', `
|
||||
${rect(66, 130, 908, 390, '#1f2933', '#111827', 12)}
|
||||
<rect x="66" y="130" width="908" height="44" rx="12" fill="#343b46"/>
|
||||
${text(100, 159, 'project: ~/src/tilix', { size: 16, fill: '#d7e3f5', family: 'monospace' })}
|
||||
${rect(88, 198, 170, 286, '#111827', '#334155', 8)}
|
||||
${text(112, 232, 'sessions', { size: 15, fill: '#94a3b8', family: 'monospace' })}
|
||||
${text(112, 272, 'build', { size: 18, fill: '#f8fafc', family: 'monospace' })}
|
||||
${text(112, 310, 'ssh prod', { size: 18, fill: '#f8fafc', family: 'monospace' })}
|
||||
${text(112, 348, 'logs', { size: 18, fill: '#f8fafc', family: 'monospace' })}
|
||||
${rect(286, 198, 308, 132, '#0f172a', '#475569', 8)}
|
||||
${rect(616, 198, 308, 132, '#0f172a', '#475569', 8)}
|
||||
${rect(286, 352, 638, 132, '#0f172a', '#475569', 8)}
|
||||
${text(316, 238, '$ dub build', { size: 19, fill: '#a7f3d0', family: 'monospace' })}
|
||||
${text(646, 238, '$ journalctl -f', { size: 19, fill: '#bfdbfe', family: 'monospace' })}
|
||||
${text(316, 394, '$ grep -R GtkD source/', { size: 19, fill: '#fde68a', family: 'monospace' })}
|
||||
${text(316, 430, 'ready', { size: 18, fill: '#e5e7eb', family: 'monospace' })}
|
||||
`);
|
||||
}
|
||||
|
||||
function gcPools() {
|
||||
return baseSvg(900, 520, 'GC D: малые пулы', 'Классы размеров и страницы памяти', '#0f766e', `
|
||||
${[16, 32, 64, 128, 256, 512].map((size, i) => {
|
||||
const y = 150 + i * 48;
|
||||
return `${text(74, y + 28, `${size} B`, { size: 17, fill: '#1f2933', family: 'monospace' })}${rect(150, y, 620, 32, '#ecfeff', '#99f6e4', 4)}${Array.from({ length: 10 }, (_, j) => `<rect x="${164 + j * 58}" y="${y + 7}" width="${38 + (j % 3) * 5}" height="18" rx="3" fill="${j % 2 ? '#14b8a6' : '#0f766e'}"/>`).join('')}`;
|
||||
}).join('')}
|
||||
${text(74, 470, 'Каждая строка показывает отдельный класс размера и фрагментацию внутри страницы.', { size: 16, fill: '#52606d' })}
|
||||
`);
|
||||
}
|
||||
|
||||
function gcLargePool() {
|
||||
return baseSvg(900, 520, 'GC D: большие объекты', 'Метаданные растут вместе с диапазоном страниц', '#c2410c', `
|
||||
${rect(86, 166, 728, 92, '#fff7ed', '#fdba74', 10)}
|
||||
${text(112, 222, 'object > 100 MB', { size: 25, fill: '#9a3412', family: 'monospace', weight: 700 })}
|
||||
${rect(86, 310, 728, 58, '#f8fafc', '#d1d8e0', 8)}
|
||||
${Array.from({ length: 18 }, (_, i) => `<rect x="${108 + i * 38}" y="323" width="24" height="32" rx="4" fill="${i < 11 ? '#fb923c' : '#cbd5e1'}"/>`).join('')}
|
||||
${text(86, 410, 'Один большой объект может оставить заметный хвост таблиц и свободных страниц.', { size: 17, fill: '#52606d' })}
|
||||
`);
|
||||
}
|
||||
|
||||
const assetFiles = {
|
||||
'cover-virus.svg': baseSvg(900, 480, 'Планировщик против рекламного запуска', 'Схема поиска вредного задания', '#2f6fed', `${rect(86, 168, 728, 178, '#eef3fb', '#bfccdc', 12)}${text(128, 230, 'taskschd.msc', { size: 38, weight: 700, fill: '#1f2933', family: 'monospace' })}${text(128, 286, 'firefox.exe + URL', { size: 28, fill: '#b42318', family: 'monospace' })}`),
|
||||
'taskschd-run.svg': taskScheduler('run'),
|
||||
'taskschd-action.svg': taskScheduler('action'),
|
||||
'jquery-webpack.svg': codeAsset('jQuery в Webpack', 'Один экземпляр библиотеки для модулей и старых плагинов', '#2563eb', ['import $ from "jquery";', 'window.$ = window.jQuery = $;', '', 'new webpack.ProvidePlugin({', ' $: "jquery",', ' jQuery: "jquery"', '});']),
|
||||
'dmd-win64.svg': diagramAsset('DMD x64 под Windows', 'Не смешиваем OMF и COFF библиотеки', '#7c2d12', [['D source', 'main.d'], ['dmd -m64', 'compiler'], ['COFF libs', 'x64'], ['EXE', 'win64']]),
|
||||
'ctfe-engine.svg': diagramAsset('NewCTFE', 'Вычисления во время компиляции без лишней магии', '#7c3aed', [['AST', 'input'], ['CTFE VM', 'evaluate'], ['Result', 'constant'], ['Binary', 'ready']]),
|
||||
'vibe-cover.svg': diagramAsset('vibe.d', 'Асинхронный веб-стек на языке D', '#059669', [['HTTP', 'server'], ['Fiber', 'async I/O'], ['Mongo/Redis', 'drivers'], ['Diet', 'views']]),
|
||||
'vibe-async.svg': featureCard('Асинхронность', 'Задачи выглядят синхронно, I/O остаётся неблокирующим', '#059669', ['fiber на запрос', 'ожидание без блокировки', 'простая модель ошибок']),
|
||||
'vibe-api.svg': featureCard('Короткий API', 'Минимум кода для типовых веб-задач', '#2563eb', ['маршруты рядом с логикой', 'простые настройки сервера', 'DUB-пакеты']),
|
||||
'vibe-web.svg': featureCard('Веб-инструменты', 'HTTP, WebSocket, сессии и шаблоны', '#dc2626', ['статические файлы', 'JSON/BSON', 'SMTP и криптография']),
|
||||
'vibe-db.svg': featureCard('Базы данных', 'MongoDB и Redis доступны из коробки', '#0f766e', ['асинхронные драйверы', 'простые запросы', 'расширение через DUB']),
|
||||
'vibe-io.svg': featureCard('Неблокирующий I/O', 'libevent и системные API вместо ожидания потока', '#9333ea', ['сетевые операции', 'файловые операции', 'меньше простаивания']),
|
||||
'vibe-dlang.svg': featureCard('Язык D', 'Скорость нативного кода и выразительность высокого уровня', '#b91c1c', ['типы и шаблоны', 'быстрая компиляция', 'удобная метапрограмма']),
|
||||
'vibe-diet.svg': featureCard('Diet templates', 'HTML без лишнего синтаксического шума', '#0f172a', ['наследник Pug/Haml', 'динамические вставки', 'читаемые view']),
|
||||
'php-ssl-cacert.svg': diagramAsset('PHP SSL CA bundle', 'Ошибка лечится сертификатами, а не отключением проверки', '#0f766e', [['PHP', 'request'], ['OpenSSL', 'verify'], ['CA file', 'cacert.pem'], ['HTTPS', 'ok']]),
|
||||
'tilix-cover.svg': diagramAsset('Tilix + D', 'Desktop-приложение на GtkD', '#7c3aed', [['GtkD', 'UI'], ['D', 'logic'], ['VTE', 'terminal'], ['GNOME', 'HIG']]),
|
||||
'tilix-screenshot.svg': tilixScreenshot(),
|
||||
'preg-match-all-d.svg': codeAsset('preg_match_all на D', 'Регулярные выражения, группы и позиции совпадений', '#be123c', ['auto r = regex(r"(\\\\w+)=(\\\\d+)");', 'auto m = matchAll("a=1 b=22", r);', '', 'foreach (hit; m) {', ' writeln(hit.captures);', '}']),
|
||||
'bitrix-offer-api.svg': codeAsset('Bitrix API: SKU', 'Создание торгового предложения и привязка к товару', '#1d4ed8', ['CModule::IncludeModule("iblock");', '$el = new CIBlockElement;', '$id = $el->Add($fields);', 'CPrice::SetBasePrice($id, 990);']),
|
||||
'bitrix-translit-api.svg': codeAsset('Bitrix API: translit', 'Символьный код из имени элемента', '#0f766e', ['$code = CUtil::translit($name, "ru", [', ' "replace_space" => "-",', ' "replace_other" => "-",', ']);']),
|
||||
'bitrix-photo-editor.svg': diagramAsset('Bitrix photo editor', 'Встроенный FileInput для загрузки и редактирования картинки', '#1d4ed8', [['FileInput', 'create'], ['upload', 'image'], ['edit', 'crop'], ['save', 'file ID']]),
|
||||
'bitrix-photo-editor-ui.svg': baseSvg(1030, 720, 'Редактор картинок Bitrix', 'Читаемый интерфейс FileInput', '#1d4ed8', `${rect(70, 150, 890, 470, '#eef3fb', '#bfccdc', 10)}${rect(110, 200, 520, 360, '#ffffff', '#9aa8b8', 8)}<path d="M160 494l132-150 102 110 70-76 116 116z" fill="#93c5fd"/><circle cx="484" cy="286" r="46" fill="#fde68a"/>${rect(668, 200, 238, 56, '#fff', '#d1d8e0', 8)}${text(690, 236, 'Обрезать', { size: 20, fill: '#1f2933' })}${rect(668, 278, 238, 56, '#fff', '#d1d8e0', 8)}${text(690, 314, 'Повернуть', { size: 20, fill: '#1f2933' })}${rect(668, 356, 238, 56, '#1d4ed8', '#1d4ed8', 8)}${text(710, 392, 'Сохранить', { size: 20, fill: '#fff', weight: 700 })}`),
|
||||
'dconf-gc-cover.svg': diagramAsset('D GC under the hood', 'Пулы, страницы, маркировка и очистка', '#0f766e', [['Pool', 'memory'], ['Mark', 'scan'], ['Sweep', 'free'], ['Reuse', 'alloc']]),
|
||||
'gc-small-pools.svg': gcPools(),
|
||||
'gc-large-pool.svg': gcLargePool(),
|
||||
'firefox-timeout.svg': baseSvg(860, 420, 'Firefox timeout', 'Когда браузеру нужно ждать дольше', '#ea580c', `${rect(80, 170, 700, 80, '#fff7ed', '#fed7aa', 12)}${text(112, 222, 'network.http.response.timeout = 600', { size: 28, fill: '#9a3412', family: 'monospace', weight: 700 })}${rect(80, 286, 520, 26, '#fed7aa', '#fed7aa', 13)}${rect(80, 286, 390, 26, '#ea580c', '#ea580c', 13)}${text(80, 352, 'Для административных операций, а не для маскировки медленного сайта.', { size: 18, fill: '#52606d' })}`),
|
||||
};
|
||||
|
||||
await mkdir(illustrationsDir, { recursive: true });
|
||||
await mkdir(mediaDir, { recursive: true });
|
||||
await mkdir(dataDir, { recursive: true });
|
||||
|
||||
for (const [name, svg] of Object.entries(assetFiles)) {
|
||||
await writeFile(join(illustrationsDir, name), svg);
|
||||
}
|
||||
|
||||
for (const name of ['cropped-WallpapersMania.nnm_.ru_vol141-087.jpg', 'vibe.d.png', 'logo.png', 'bitrix-foto-editor-1.jpg', 'Без-имени.png', '-имени-e1495740674880.png', 'i-880x660.jpg']) {
|
||||
try {
|
||||
await copyFile(join(sourceRoot, 'assets', name), join(mediaDir, name));
|
||||
} catch {
|
||||
// Generated illustrations are enough for the site; copied media is optional.
|
||||
}
|
||||
}
|
||||
|
||||
const articles = rawArticles.filter((article) => article.slug !== 'привет-мир').map((article) => {
|
||||
const content = normalizeContent(article.contentHtml);
|
||||
const addition = articleAdditions[article.slug]?.trim() ?? '';
|
||||
const contentHtml = [content, addition].filter(Boolean).join('\n');
|
||||
return {
|
||||
slug: article.slug,
|
||||
title: article.title,
|
||||
date: article.date,
|
||||
author: 'DarkRiDDeR',
|
||||
categories: categories[article.slug] ?? [],
|
||||
cover: covers[article.slug],
|
||||
excerpt: article.paragraphs.join(' ').replace(/\s+/g, ' ').slice(0, 260),
|
||||
contentHtml,
|
||||
readingMinutes: Math.max(1, Math.round(stripTags(contentHtml).split(/\s+/).length / 180)),
|
||||
};
|
||||
}).sort((a, b) => new Date(b.date) - new Date(a.date));
|
||||
|
||||
await writeFile(join(dataDir, 'articles.json'), `${JSON.stringify(articles, null, 2)}\n`);
|
||||
console.log(`Generated ${articles.length} articles and ${Object.keys(assetFiles).length} illustration assets`);
|
||||