Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ce449320c |
No files matched your search
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "8.5.0"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"transform-react-jsx",
|
||||
"transform-object-rest-spread",
|
||||
"transform-class-properties"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
pipeline:
|
||||
build:
|
||||
image: docker
|
||||
commands:
|
||||
- docker build -t tmshv/hudozka:latest -t tmshv/hudozka:build-${DRONE_BUILD_NUMBER} .
|
||||
- docker save -o hudozka-docker_container${DRONE_BUILD_NUMBER}.tar tmshv/hudozka:build-${DRONE_BUILD_NUMBER}
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
|
||||
scp:
|
||||
image: appleboy/drone-scp
|
||||
host: art.shlisselburg.org
|
||||
username: master
|
||||
target: ~/Hudozka
|
||||
source: hudozka-docker_container${DRONE_BUILD_NUMBER}.tar
|
||||
secrets: [scp_key]
|
||||
when:
|
||||
branch: master
|
||||
@@ -0,0 +1,2 @@
|
||||
MONGO_HUDOZKA_PASSWORD=h123
|
||||
HUDOZKA_DB_URI=mongodb://hudozka:h123@mongo:27017/hudozka?authSource=admin
|
||||
@@ -0,0 +1,30 @@
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true
|
||||
},
|
||||
"extends": "standard",
|
||||
"plugins": [
|
||||
"standard",
|
||||
"promise",
|
||||
"react",
|
||||
],
|
||||
"globals": {
|
||||
"angular": true,
|
||||
"DISQUS": true
|
||||
},
|
||||
"rules": {
|
||||
"semi": ["error", "never"],
|
||||
"space-before-function-paren": "off",
|
||||
"comma-dangle": "off",
|
||||
"no-tabs": "off",
|
||||
"indent": ["error", "tab"],
|
||||
"react/jsx-uses-react": "error",
|
||||
"react/jsx-uses-vars": "error",
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
## Summary
|
||||
|
||||
-
|
||||
|
||||
## Closes #
|
||||
@@ -1,53 +0,0 @@
|
||||
name: Build Frontend and Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.PAT }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./
|
||||
file: ./apps/hudozka/Dockerfile
|
||||
push: true
|
||||
platforms: linux/amd64
|
||||
tags: |
|
||||
ghcr.io/tmshv/hudozka-app:latest
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache-new
|
||||
|
||||
- name: Move cache
|
||||
run: |
|
||||
rm -rf /tmp/.buildx-cache
|
||||
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
||||
@@ -1,31 +0,0 @@
|
||||
name: Build Pocketbase and Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "pb/**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.PAT }}
|
||||
|
||||
- name: Build and Push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
push: true
|
||||
context: pb
|
||||
tags: |
|
||||
ghcr.io/tmshv/hudozka-pb:latest
|
||||
@@ -1,92 +0,0 @@
|
||||
name: Test App
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Lint
|
||||
run: npx biome ci
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
typecheck:
|
||||
name: Type Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Type check
|
||||
run: npx tsc --noEmit -p apps/hudozka/tsconfig.json
|
||||
|
||||
build:
|
||||
name: Build
|
||||
needs: [lint, test, typecheck]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Cache Next.js build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .next/cache
|
||||
key: nextjs-${{ hashFiles('package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx', '**/*.css') }}
|
||||
restore-keys: nextjs-${{ hashFiles('package-lock.json') }}-
|
||||
|
||||
- name: Disable Next.js telemetry
|
||||
run: npx next telemetry disable
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
@@ -5,28 +5,15 @@ npm-debug.log
|
||||
|
||||
private*.json
|
||||
|
||||
node_modules
|
||||
/node_modules
|
||||
/bower_components
|
||||
/modules/hudozka-api-v1/lib
|
||||
/public
|
||||
/build
|
||||
/out
|
||||
dist
|
||||
**/env
|
||||
**/venv
|
||||
**/__pycache__
|
||||
**/*.pyc
|
||||
/coverage
|
||||
**/.vscode
|
||||
*.log
|
||||
.now
|
||||
.vercel
|
||||
.next
|
||||
tsconfig.tsbuildinfo
|
||||
.env
|
||||
|
||||
pb/pb_data/*.db*
|
||||
pb/pb_data/storage
|
||||
|
||||
# ralphex progress logs
|
||||
.ralphex/progress/
|
||||
|
||||
.superpowers/
|
||||
@@ -1,124 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Website for the Art School of Shlisselburg ([art.shlisselburg.org](https://art.shlisselburg.org)). Content is in Russian. Built with Next.js using the App Router and PocketBase as the backend (migrated from Strapi — see `pb/` and `src/remote/pb.ts`). The project has migrated through Express → Angular 1 → React class components → Next.js (Pages Router) → Next.js (App Router); some legacy artifacts remain.
|
||||
|
||||
## Commands
|
||||
|
||||
From repo root (delegates to `apps/hudozka`):
|
||||
|
||||
- `npm run dev` — dev server with Turbo (`next dev --turbo`)
|
||||
- `npm run build` — production build
|
||||
- `npm run lint` — Biome lint + format check across the whole monorepo (`biome check`)
|
||||
- `npm run format` — auto-fix lint and format issues (`biome check --write`)
|
||||
- `npm test` — run unit tests (Vitest) across every workspace
|
||||
|
||||
Per-app:
|
||||
|
||||
- `npm run dev -w apps/hudozka-writer` — writer UI (Vite)
|
||||
- `npm run migrate -w apps/migrate-to-pocketbase` — one-off migration script
|
||||
|
||||
Node 24 required (see `mise.toml`). Deployed on Vercel.
|
||||
|
||||
## Git Workflow
|
||||
|
||||
Feature branches are named `issue-XXX` where `XXX` is the related GitHub issue number. PR descriptions should not include a test plan section.
|
||||
|
||||
## CI/CD
|
||||
|
||||
- `apps/hudozka/Dockerfile` — multi-stage Alpine build with standalone Next.js output, pushed to GHCR. Build context is the repo root so the workspace graph is available.
|
||||
- `.github/workflows/build-app.yaml` — builds and pushes Docker image on push to master (`file: ./apps/hudozka/Dockerfile`, `context: ./`).
|
||||
- `.github/workflows/test-app.yml` — runs lint, tests, typecheck, and build on PRs.
|
||||
|
||||
## Code Style
|
||||
|
||||
Biome (`biome.json`) handles formatting and linting. Style: no semicolons, double quotes, 4-space indentation, always-multiline trailing commas, LF line endings, 120-column line width. Lint scope is limited to `**/src/**/*.{ts,tsx,js}` via `files.includes`. Console calls are restricted to `console.error`/`console.warn` (full `console.*` is allowed only inside `apps/migrate-to-pocketbase`). `noDangerouslySetInnerHtml` is disabled because the site renders trusted CMS HTML. Inline `biome-ignore` / `eslint-disable` comments are not used — fix the underlying issue instead.
|
||||
|
||||
**React components:** Use regular function declarations (not arrow functions) and `type` for props. Do not use `React.FC`.
|
||||
|
||||
```tsx
|
||||
export type MyComponentProps = {
|
||||
value: string
|
||||
}
|
||||
|
||||
export function MyComponent({ value }: MyComponentProps) {
|
||||
return <div>{value}</div>
|
||||
}
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
When adding or updating packages in `package.json`, do not specify the PATCH version — use `^MAJOR.MINOR.0`. The exact patch version is managed by the lockfile and `npm update`.
|
||||
|
||||
## Architecture
|
||||
|
||||
**App Router with SSG/ISR:** Pages are server components under `src/app/` with `export const revalidate = 30`. The root layout (`src/app/layout.tsx`) fetches menu data and passes it down. The catch-all route `src/app/[...slug]/page.tsx` handles all dynamic content pages with `generateStaticParams` and `generateMetadata`.
|
||||
|
||||
**Server/client boundary:** Layout and page components are server components that fetch data. The `App` component (`src/components/App`) is a `"use client"` component that provides the layout chrome (navigation, breadcrumbs, footer). Content is passed as `children` from server pages through the client boundary. Components using hooks (`PageGrid`, `HomeContent`, `PageContent`, `Menu`, `MobileNavigation`, `Wrapper`) are marked `"use client"`.
|
||||
|
||||
**Data flow:** PocketBase API → `src/remote/api.ts` fetches data via `apiGet()` generic wrapper → `src/remote/factory.ts` normalizes responses into app types (`src/types.ts`) → pages render token-based content blocks. The API layer uses a factory pattern with default-response fallbacks on failure. The legacy Strapi integration was removed (see commit "rm strapi"); `src/remote/pb.ts` is the PocketBase client.
|
||||
|
||||
**Metadata:** Uses the Next.js Metadata API (`generateMetadata`, `export const metadata`). The `buildMetadata()` helper in `src/lib/meta.ts` bridges the app's `Meta` type to Next.js `Metadata` format.
|
||||
|
||||
**Content tokens:** Pages are composed of flexible content blocks (tokens): `text`, `image`, `file`, `html`, `youtube`, `instagram`, `grid`. The `Token` discriminated union is defined in `src/types.ts`. Each token type maps to a renderer component in the `PageContent` client component.
|
||||
|
||||
**State management:** Valtio for reactive state (`src/store/`). Theme/accessibility preferences in `theme` store, playback state in `play` store.
|
||||
|
||||
## Key Directories
|
||||
|
||||
- `apps/hudozka/src/app/` — App Router pages, layouts, route handlers (catch-all, not-found, sitemap, robots, feed)
|
||||
- `apps/hudozka/src/components/` — page-level and feature components (folder-per-component + `styles.module.css`)
|
||||
- `apps/hudozka/src/ui/` — Next/store-coupled UI that stays in the app: `Picture`, `ThemeColor`
|
||||
- `apps/hudozka/src/remote/` — PocketBase API client (`api.ts`, `factory.ts`, `types.ts`, `pb.ts`)
|
||||
- `apps/hudozka/src/store/` — Valtio stores (theme/accessibility, playback)
|
||||
- `apps/hudozka/src/hooks/` — business-coupled hooks: `useDarkTheme`, `useAccessibility`
|
||||
- `apps/hudozka/src/lib/` — app-only lib (currently just `meta.ts`)
|
||||
- `apps/hudozka/src/style/` — global CSS with CSS custom properties for theming
|
||||
- `packages/ui/` — `@hudozka/ui` — reusable primitives (Box, Button, Panel, Title, Overlay)
|
||||
- `packages/hooks/` — `@hudozka/hooks` — generic React hooks (useToggle, useLockBodyScroll, useMediaQuery, useMobile, useReducedMotion)
|
||||
- `packages/utils/` — `@hudozka/utils` — pure utilities (array, string, url, math, image, file, date)
|
||||
- `packages/text/` — `@hudozka/text` — markdown + typograf rendering
|
||||
- `apps/hudozka-writer/` — Vite-based Tiptap editor
|
||||
- `apps/migrate-to-pocketbase/` — one-off Strapi → PocketBase migration script
|
||||
- `pb/` — PocketBase instance (data, migrations)
|
||||
|
||||
## Styling
|
||||
|
||||
CSS Modules (`.module.css`) for component-scoped styles. Global theme via CSS custom properties in `src/style/style.css`. No utility CSS framework. Uses `classnames` package for conditional class composition.
|
||||
|
||||
## TypeScript
|
||||
|
||||
Path alias: `@/*` → `src/*`. Strict mode enabled. Target ES2020. `moduleDetection: "force"` in tsconfig (required because Next.js auto-generates `.next/types/validator.ts` without exports, which breaks `verbatimModuleSyntax`). Use `@/` path alias for all local imports, not `src/`. Legacy `src/` imports exist in the codebase but should not be used in new or modified code. The `@/*` path alias is only configured in `apps/hudozka/tsconfig.json`; packages under `packages/*` use short relative imports.
|
||||
|
||||
`verbatimModuleSyntax` is enabled — use `import type` for type-only imports. When importing both values and types from the same module, use separate `import` and `import type` statements:
|
||||
|
||||
```ts
|
||||
import { createPage } from "@/remote/factory"
|
||||
import type { Page } from "@/types"
|
||||
```
|
||||
|
||||
## Accessibility
|
||||
|
||||
The site has an accessibility panel with configurable font size (8-48pt), color schemes (5 presets: black-on-white, white-on-black, blue-on-blue, brown-on-yellow, green-on-brown), line height, letter spacing, image toggling, and serif/sans-serif font choice — all managed through the Valtio theme store. The theme store currently applies changes via direct DOM manipulation in a Valtio `subscribe()` callback (`src/store/theme.ts`).
|
||||
|
||||
## Images
|
||||
|
||||
Remote images from `hudozkacdn.tmshv.com` with `images.weserv.nl` as proxy. Blur hash encoding for progressive loading (uses `sharp` server-side). Image optimization configured in `next.config.ts` via `remotePatterns`.
|
||||
|
||||
## Testing
|
||||
|
||||
Vitest for unit tests. Config in `vitest.config.ts` with `@/` and `src/` path aliases. Test files are co-located with source across `src/lib/`, `src/ui/`, and `src/components/`. Covers utility functions (array, string, url, file, image, date, text, meta), UI primitives (Box, Button, Panel), and components (Breadcrumbs, YMetrika).
|
||||
|
||||
## Known Legacy / Technical Debt
|
||||
|
||||
Active modernization is tracked in GitHub issues #222–#238. Key items:
|
||||
|
||||
- `src/types.ts` has `I`-prefix interfaces (`IPage`, `IMenu`, etc.) from the Angular era alongside modern `type` aliases. The `tokens` field on `IPage` is `any[]` despite a proper `Token` union type existing.
|
||||
- `src/store/theme.ts` manipulates DOM directly outside React lifecycle (sets CSS vars on `document.documentElement`, toggles `document.body` classes).
|
||||
- Two orphaned `.scss` files in `src/style/` (`tape-viewer.scss`, `vertical-image.scss`) — not imported anywhere, no SCSS preprocessor configured.
|
||||
- Some inline styles in `src/components/App/index.tsx` should be CSS modules.
|
||||
- `react-use` and `use-media` dependencies are unmaintained; only `useToggle` and `useLockBodyScroll` are used from `react-use`.
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Build
|
||||
FROM node:10 as build
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm i
|
||||
COPY webpack.config.js gulpfile.js .babelrc ./
|
||||
COPY robots.txt ./
|
||||
COPY src ./src
|
||||
RUN npm run gulp
|
||||
RUN npm run build-app
|
||||
RUN npm run out
|
||||
|
||||
|
||||
# Run
|
||||
FROM node:8-alpine
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm i --production
|
||||
|
||||
COPY --from=build /app/out ./src
|
||||
COPY --from=build /app/public ./public
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD npm start
|
||||
@@ -1,68 +1,46 @@
|
||||
# [ART.SHLISSELBURG.ORG](https://art.shlisselburg.org)
|
||||
|
||||
Website for the Art School of Shlisselburg. Built with Next.js and powered by PocketBase (migrated from Strapi).
|
||||
Site of Art school of Shlisselburg
|
||||
|
||||
Font: [«Shadow»](http://mishapanfilov.ru/font_shadow.html)
|
||||
## Usage
|
||||
|
||||
## Repo layout
|
||||
### Build
|
||||
|
||||
This is an npm-workspaces monorepo:
|
||||
|
||||
- `apps/hudozka` — main Next.js app (art.shlisselburg.org)
|
||||
- `apps/hudozka-writer` — Vite-based content editor
|
||||
- `apps/migrate-to-pocketbase` — one-off migration script (Strapi → PocketBase)
|
||||
- `packages/ui` — `@hudozka/ui` — shared React primitives (Box, Button, Panel, Title, Overlay)
|
||||
- `packages/hooks` — `@hudozka/hooks` — generic React hooks
|
||||
- `packages/utils` — `@hudozka/utils` — pure utilities (array, string, url, math, image, file, date)
|
||||
- `packages/text` — `@hudozka/text` — markdown + typograf rendering
|
||||
- `pb/` — PocketBase instance (untouched by the monorepo migration)
|
||||
|
||||
Common commands (from repo root):
|
||||
|
||||
- `npm run dev` — run the web app dev server
|
||||
- `npm run build` — production build of the web app
|
||||
- `npm run lint` — lint the whole monorepo
|
||||
- `npm test` — run tests across every workspace
|
||||
- `npm run dev -w apps/hudozka-writer` — run the writer
|
||||
- `npm run migrate -w apps/migrate-to-pocketbase` — run the migration script
|
||||
|
||||
## Development
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js 24 (see `mise.toml`)
|
||||
|
||||
### Getting Started
|
||||
|
||||
1. Install dependencies
|
||||
```bash
|
||||
npm install
|
||||
$ docker build -t tmshv/hudozka .
|
||||
```
|
||||
|
||||
2. Start development server
|
||||
```bash
|
||||
npm run dev
|
||||
### How to run
|
||||
|
||||
#### Add .env file
|
||||
|
||||
```
|
||||
MONGO_HUDOZKA_PASSWORD=<strong_password>
|
||||
HUDOZKA_DB_URI=mongodb://hudozka:strong_password@mongo:27017/hudozka?authSource=admin
|
||||
```
|
||||
|
||||
The development server runs at [http://localhost:3000](http://localhost:3000) with Turbo mode enabled.
|
||||
#### Run
|
||||
```bash
|
||||
$ docker-compose up -d
|
||||
```
|
||||
|
||||
## Content Management
|
||||
Content is fetched from PocketBase and rendered using flexible content tokens:
|
||||
- `text` — Markdown with Russian typography (Typograf)
|
||||
- `image` — Responsive images with blur hash
|
||||
- `file` — Document downloads
|
||||
- `html` — Raw HTML embeds
|
||||
- `youtube` — Video embeds
|
||||
- `instagram` — Social media embeds
|
||||
- `grid` — Card grids
|
||||
### How to sync data base
|
||||
|
||||
## Accessibility
|
||||
The site includes an accessibility panel with configurable:
|
||||
- Font size (8-48pt)
|
||||
- Color schemes (5 presets: black-on-white, white-on-black, blue-on-blue, brown-on-yellow, green-on-brown)
|
||||
- Line height and letter spacing
|
||||
- Image toggling
|
||||
- Font family (serif/sans-serif)
|
||||
Local DB to remote DB
|
||||
|
||||
```bash
|
||||
DB=hudozka
|
||||
HOST=<ip_address>
|
||||
USER=<mongo_user>
|
||||
PWD=<mongo_password>
|
||||
mongodump -d ${DB} --gzip --archive | mongorestore -h ${HOST} -u ${USER} -p ${PWD} -d ${DB} --gzip --archive
|
||||
```
|
||||
|
||||
## Based on:
|
||||
- [NodeJS](http://nodejs.org) 10.0
|
||||
- [koa.js](http://koajs.com) 2.0
|
||||
- [React](https://react.org) 16.3
|
||||
- [MongoDB](http://mongodb.org) 3.6
|
||||
|
||||
## Contributors
|
||||
- Roman Timashev ([roman@tmshv.ru](mailto:roman@tmshv.ru))
|
||||
@@ -1,24 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,30 +0,0 @@
|
||||
# React + TypeScript + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||
|
||||
- Configure the top-level `parserOptions` property like this:
|
||||
|
||||
```js
|
||||
export default {
|
||||
// other rules...
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
project: ['./tsconfig.json', './tsconfig.node.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
|
||||
- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
|
||||
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
|
||||
@@ -1,12 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Hudozka Writer</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,45 +0,0 @@
|
||||
{
|
||||
"name": "hudozka-writer",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc --noEmit && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tiptap/core": "^3.0.0",
|
||||
"@tiptap/extension-blockquote": "^3.0.0",
|
||||
"@tiptap/extension-bold": "^3.0.0",
|
||||
"@tiptap/extension-bullet-list": "^3.0.0",
|
||||
"@tiptap/extension-code": "^3.0.0",
|
||||
"@tiptap/extension-code-block": "^3.0.0",
|
||||
"@tiptap/extension-document": "^3.0.0",
|
||||
"@tiptap/extension-hard-break": "^3.0.0",
|
||||
"@tiptap/extension-heading": "^3.0.0",
|
||||
"@tiptap/extension-horizontal-rule": "^3.0.0",
|
||||
"@tiptap/extension-italic": "^3.0.0",
|
||||
"@tiptap/extension-link": "^3.0.0",
|
||||
"@tiptap/extension-list-item": "^3.0.0",
|
||||
"@tiptap/extension-ordered-list": "^3.0.0",
|
||||
"@tiptap/extension-paragraph": "^3.0.0",
|
||||
"@tiptap/extension-strike": "^3.0.0",
|
||||
"@tiptap/extension-text": "^3.0.0",
|
||||
"@tiptap/markdown": "^3.0.0",
|
||||
"@tiptap/pm": "^3.0.0",
|
||||
"@tiptap/react": "^3.0.0",
|
||||
"@tiptap/starter-kit": "^3.0.0",
|
||||
"@tiptap/suggestion": "^3.22.2",
|
||||
"pocketbase": "^0.25.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.0",
|
||||
"@vitejs/plugin-react": "^5.2.0",
|
||||
"typescript": "^5.9.0",
|
||||
"vite": "^8.0.16"
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
.app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.app-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 20px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.app-slug {
|
||||
color: #888;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.app-header button {
|
||||
padding: 4px 12px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.app-header button:first-of-type {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.app-main {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.app-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.app-error {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
.app-published-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.app-published-toggle input {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
import { Editor } from "./components/Editor"
|
||||
import { Login } from "./components/Login"
|
||||
import { pb } from "./pb"
|
||||
import type { PbPage } from "./types"
|
||||
import "./App.css"
|
||||
|
||||
function getSlugFromUrl(): string | null {
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
return params.get("page")
|
||||
}
|
||||
|
||||
export function App() {
|
||||
const [authenticated, setAuthenticated] = useState(pb.authStore.isValid)
|
||||
const [page, setPage] = useState<PbPage | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState("")
|
||||
|
||||
const slug = getSlugFromUrl()
|
||||
|
||||
const loadPage = useCallback(async (slug: string) => {
|
||||
setLoading(true)
|
||||
setError("")
|
||||
try {
|
||||
const record = await pb.collection("pages").getFirstListItem<PbPage>(`slug="${slug}"`)
|
||||
setPage(record)
|
||||
} catch {
|
||||
setError(`Page "${slug}" not found`)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (authenticated && slug) {
|
||||
loadPage(slug)
|
||||
}
|
||||
}, [authenticated, slug, loadPage])
|
||||
|
||||
function handleLogout() {
|
||||
pb.authStore.clear()
|
||||
setAuthenticated(false)
|
||||
setPage(null)
|
||||
}
|
||||
|
||||
async function handleDuplicate() {
|
||||
if (!page) return
|
||||
const newSlug = `${page.slug}-copy`
|
||||
try {
|
||||
const record = await pb.collection("pages").create<PbPage>({
|
||||
title: `${page.title} (copy)`,
|
||||
slug: newSlug,
|
||||
excerpt: page.excerpt,
|
||||
date: page.date,
|
||||
cover: page.cover,
|
||||
doc: page.doc,
|
||||
tags: page.tags,
|
||||
published: false,
|
||||
draft: null,
|
||||
})
|
||||
window.location.search = `?page=${record.slug}`
|
||||
} catch (err) {
|
||||
console.error("Duplicate failed:", err)
|
||||
alert("Duplicate failed. Check console.")
|
||||
}
|
||||
}
|
||||
|
||||
async function handleTogglePublished() {
|
||||
if (!page) return
|
||||
try {
|
||||
const updated = await pb.collection("pages").update<PbPage>(page.id, {
|
||||
published: !page.published,
|
||||
})
|
||||
setPage(updated)
|
||||
} catch (err) {
|
||||
console.error("Toggle published failed:", err)
|
||||
alert("Toggle published failed. Check console.")
|
||||
}
|
||||
}
|
||||
|
||||
if (!authenticated) {
|
||||
return <Login onLogin={() => setAuthenticated(true)} />
|
||||
}
|
||||
|
||||
if (!slug) {
|
||||
return (
|
||||
<div className="app-message">
|
||||
<p>
|
||||
Add <code>?page=slug</code> to the URL to edit a page.
|
||||
</p>
|
||||
<button type="button" onClick={handleLogout}>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <div className="app-message">Loading...</div>
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="app-message">
|
||||
<p className="app-error">{error}</p>
|
||||
<button type="button" onClick={handleLogout}>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!page) {
|
||||
return <div className="app-message">Loading...</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
<header className="app-header">
|
||||
<span className="app-title">{page.title}</span>
|
||||
<span className="app-slug">/{page.slug}</span>
|
||||
<label className="app-published-toggle">
|
||||
<input type="checkbox" checked={page.published} onChange={handleTogglePublished} />
|
||||
Published
|
||||
</label>
|
||||
<button type="button" onClick={handleDuplicate}>
|
||||
Duplicate
|
||||
</button>
|
||||
<button type="button" onClick={handleLogout}>
|
||||
Logout
|
||||
</button>
|
||||
</header>
|
||||
<main className="app-main">
|
||||
<Editor page={page} />
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
.block-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
flex-shrink: 0;
|
||||
width: 28px;
|
||||
padding-top: 2px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.block-action-btn {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
color: #ccc;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.15s ease;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.block-action-btn:hover {
|
||||
border-color: #ddd;
|
||||
color: #666;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.block-action-delete:hover {
|
||||
border-color: #c00;
|
||||
color: #c00;
|
||||
background: #fff0f0;
|
||||
}
|
||||
|
||||
.block-action-split:hover {
|
||||
border-color: #2563eb;
|
||||
color: #2563eb;
|
||||
background: #eff6ff;
|
||||
}
|
||||
|
||||
.block-drag-handle {
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.block-drag-handle:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
[data-node-view-wrapper] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.node-view-block {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.node-view-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
import { Fragment } from "@tiptap/pm/model"
|
||||
import type { Editor } from "@tiptap/react"
|
||||
import { useCallback, useEffect } from "react"
|
||||
import "./BlockActions.css"
|
||||
|
||||
export type BlockActionsProps = {
|
||||
editor: Editor
|
||||
getPos: () => number | undefined
|
||||
showSplit?: boolean
|
||||
}
|
||||
|
||||
export function BlockActions({ editor, getPos, showSplit }: BlockActionsProps) {
|
||||
const getWrapper = useCallback(() => {
|
||||
const pos = getPos()
|
||||
if (typeof pos !== "number") return null
|
||||
const dom = editor.view.nodeDOM(pos)
|
||||
if (!dom || !(dom instanceof HTMLElement)) return null
|
||||
return dom.closest("[data-node-view-wrapper]") as HTMLElement | null
|
||||
}, [editor, getPos])
|
||||
|
||||
useEffect(() => {
|
||||
const checkActive = () => {
|
||||
const wrapper = getWrapper()
|
||||
if (!wrapper) return
|
||||
|
||||
const pos = getPos()
|
||||
if (typeof pos !== "number") {
|
||||
wrapper.classList.remove("active-block")
|
||||
return
|
||||
}
|
||||
|
||||
const { from } = editor.state.selection
|
||||
const resolved = editor.state.doc.resolve(from)
|
||||
if (resolved.depth < 1) {
|
||||
wrapper.classList.remove("active-block")
|
||||
return
|
||||
}
|
||||
|
||||
const blockPos = resolved.before(1)
|
||||
if (blockPos === pos) {
|
||||
wrapper.classList.add("active-block")
|
||||
} else {
|
||||
wrapper.classList.remove("active-block")
|
||||
}
|
||||
}
|
||||
|
||||
const handleBlur = () => {
|
||||
const wrapper = getWrapper()
|
||||
wrapper?.classList.remove("active-block")
|
||||
}
|
||||
|
||||
checkActive()
|
||||
editor.on("selectionUpdate", checkActive)
|
||||
editor.on("focus", checkActive)
|
||||
editor.on("blur", handleBlur)
|
||||
return () => {
|
||||
editor.off("selectionUpdate", checkActive)
|
||||
editor.off("focus", checkActive)
|
||||
editor.off("blur", handleBlur)
|
||||
const wrapper = getWrapper()
|
||||
wrapper?.classList.remove("active-block")
|
||||
}
|
||||
}, [editor, getPos, getWrapper])
|
||||
|
||||
const handleDelete = (e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
const pos = getPos()
|
||||
if (typeof pos !== "number") return
|
||||
const node = editor.state.doc.nodeAt(pos)
|
||||
if (!node) return
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange({
|
||||
from: pos,
|
||||
to: pos + node.nodeSize,
|
||||
})
|
||||
.run()
|
||||
}
|
||||
|
||||
const handleSplit = (e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
const pos = getPos()
|
||||
if (typeof pos !== "number") return
|
||||
const node = editor.state.doc.nodeAt(pos)
|
||||
if (!node || node.childCount < 2) return
|
||||
|
||||
const { from } = editor.state.selection
|
||||
let childOffset = pos + 1
|
||||
let splitAfter = -1
|
||||
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
const child = node.child(i)
|
||||
const childEnd = childOffset + child.nodeSize
|
||||
if (from >= childOffset && from < childEnd) {
|
||||
splitAfter = i
|
||||
break
|
||||
}
|
||||
childOffset = childEnd
|
||||
}
|
||||
|
||||
if (splitAfter < 0 || splitAfter >= node.childCount - 1) return
|
||||
|
||||
const firstChildren = []
|
||||
const secondChildren = []
|
||||
for (let i = 0; i < node.childCount; i++) {
|
||||
if (i <= splitAfter) {
|
||||
firstChildren.push(node.child(i))
|
||||
} else {
|
||||
secondChildren.push(node.child(i))
|
||||
}
|
||||
}
|
||||
|
||||
const { schema } = editor.state
|
||||
const textBlockType = schema.nodes.textBlock
|
||||
const first = textBlockType.create(null, firstChildren)
|
||||
const second = textBlockType.create(null, secondChildren)
|
||||
|
||||
const tr = editor.state.tr
|
||||
tr.replaceWith(pos, pos + node.nodeSize, Fragment.from([first, second]))
|
||||
editor.view.dispatch(tr)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="block-actions" contentEditable={false}>
|
||||
<button
|
||||
type="button"
|
||||
className="block-action-btn block-action-delete"
|
||||
title="Delete block"
|
||||
onClick={handleDelete}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
{showSplit && (
|
||||
<button
|
||||
type="button"
|
||||
className="block-action-btn block-action-split"
|
||||
title="Split block"
|
||||
onClick={handleSplit}
|
||||
>
|
||||
✂
|
||||
</button>
|
||||
)}
|
||||
<div className="block-action-btn block-drag-handle" data-drag-handle draggable title="Drag to reorder">
|
||||
⠇
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
.block-insert {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 8px 0 8px 28px;
|
||||
}
|
||||
|
||||
.block-insert-btn {
|
||||
position: relative;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid transparent;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
color: #ccc;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.15s ease;
|
||||
padding: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.block-insert-btn:hover {
|
||||
border-color: #ddd;
|
||||
color: #666;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.block-insert-menu {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
left: 56px;
|
||||
z-index: 50;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import type { Editor } from "@tiptap/react"
|
||||
import { useState } from "react"
|
||||
import { BlockMenu } from "./BlockMenu"
|
||||
import "./BlockInsert.css"
|
||||
|
||||
export type BlockInsertProps = {
|
||||
editor: Editor
|
||||
getPos: () => number | undefined
|
||||
}
|
||||
|
||||
export function BlockInsert({ editor, getPos }: BlockInsertProps) {
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
|
||||
const insertPos = (() => {
|
||||
const pos = getPos()
|
||||
if (typeof pos !== "number") return 0
|
||||
const node = editor.state.doc.nodeAt(pos)
|
||||
return node ? pos + node.nodeSize : 0
|
||||
})()
|
||||
|
||||
const handleToggle = (e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
setMenuOpen(!menuOpen)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
setMenuOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="block-insert" contentEditable={false}>
|
||||
<button type="button" className="block-insert-btn" title="Add block" onClick={handleToggle}>
|
||||
+
|
||||
</button>
|
||||
{menuOpen && (
|
||||
<div className="block-insert-menu">
|
||||
<BlockMenu editor={editor} position={insertPos} onClose={handleClose} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
.block-menu {
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 4px 0;
|
||||
min-width: 160px;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.block-menu-item {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: none;
|
||||
background: none;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.block-menu-item:hover {
|
||||
background: #f0f4ff;
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import type { Editor } from "@tiptap/react"
|
||||
import { generateBlockId } from "../lib/id"
|
||||
import "./BlockMenu.css"
|
||||
|
||||
type BlockType = {
|
||||
label: string
|
||||
type: string
|
||||
attrs?: Record<string, unknown>
|
||||
}
|
||||
|
||||
const BLOCK_TYPES: BlockType[] = [
|
||||
{ label: "Text", type: "textBlock" },
|
||||
{ label: "Image", type: "imageBlock", attrs: { id: "", imageId: "", wide: false, caption: "" } },
|
||||
{ label: "File", type: "documentBlock", attrs: { id: "", fileId: "", title: "" } },
|
||||
{ label: "Embed", type: "embedBlock", attrs: { id: "", src: "" } },
|
||||
{ label: "Card Grid", type: "cardGridBlock", attrs: { id: "", items: "[]" } },
|
||||
]
|
||||
|
||||
export type BlockMenuProps = {
|
||||
editor: Editor
|
||||
position: number
|
||||
onClose: () => void
|
||||
filter?: string
|
||||
}
|
||||
|
||||
export function BlockMenu({ editor, position, onClose, filter }: BlockMenuProps) {
|
||||
const filtered = filter
|
||||
? BLOCK_TYPES.filter(b => b.label.toLowerCase().includes(filter.toLowerCase()))
|
||||
: BLOCK_TYPES
|
||||
|
||||
function handleInsert(block: BlockType) {
|
||||
const id = generateBlockId()
|
||||
if (block.type === "textBlock") {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.insertContentAt(position, {
|
||||
type: "textBlock",
|
||||
content: [{ type: "paragraph" }],
|
||||
})
|
||||
.run()
|
||||
} else {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.insertContentAt(position, {
|
||||
type: block.type,
|
||||
attrs: { ...block.attrs, id },
|
||||
})
|
||||
.run()
|
||||
}
|
||||
onClose()
|
||||
}
|
||||
|
||||
if (filtered.length === 0) return null
|
||||
|
||||
return (
|
||||
<div className="block-menu">
|
||||
{filtered.map(block => (
|
||||
<button type="button" key={block.type} className="block-menu-item" onClick={() => handleInsert(block)}>
|
||||
{block.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export type { BlockType }
|
||||
export { BLOCK_TYPES }
|
||||
@@ -1,24 +0,0 @@
|
||||
.draft-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 8px 12px;
|
||||
background: #fff8e1;
|
||||
border: 1px solid #ffe082;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 12px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.draft-banner-discard {
|
||||
padding: 4px 12px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.draft-banner-discard:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import "./DraftBanner.css"
|
||||
|
||||
export type DraftBannerProps = {
|
||||
onDiscard: () => void
|
||||
}
|
||||
|
||||
export function DraftBanner({ onDiscard }: DraftBannerProps) {
|
||||
return (
|
||||
<div className="draft-banner">
|
||||
<span>You have unpublished changes</span>
|
||||
<button type="button" onClick={onDiscard} className="draft-banner-discard">
|
||||
Discard
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
.editor {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.editor-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.editor-content .tiptap {
|
||||
outline: none;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.editor-content .tiptap > * + * {
|
||||
margin-top: 0.75em;
|
||||
}
|
||||
|
||||
.editor-content .tiptap h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.editor-content .tiptap h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.editor-content .tiptap h3 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.editor-content .tiptap blockquote {
|
||||
border-left: 3px solid #e0e0e0;
|
||||
padding-left: 1rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.editor-content .tiptap pre {
|
||||
background: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.editor-content .tiptap code {
|
||||
background: #f0f0f0;
|
||||
border-radius: 2px;
|
||||
padding: 0.1em 0.3em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.editor-content .tiptap hr {
|
||||
border: none;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
|
||||
.editor-content .tiptap a {
|
||||
color: #2563eb;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.editor-content .tiptap ul,
|
||||
.editor-content .tiptap ol {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
.editor-markdown {
|
||||
width: 100%;
|
||||
min-height: 400px;
|
||||
padding: 16px;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
resize: vertical;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.editor-markdown:focus {
|
||||
border-color: #213547;
|
||||
}
|
||||
|
||||
.editor-content .tiptap [data-node-view-wrapper].active-block > .node-view-content {
|
||||
outline: 2px solid #d0d5dd;
|
||||
outline-offset: 2px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
import Link from "@tiptap/extension-link"
|
||||
import { Markdown } from "@tiptap/markdown"
|
||||
import { EditorContent, useEditor } from "@tiptap/react"
|
||||
import StarterKit from "@tiptap/starter-kit"
|
||||
import { useCallback, useRef, useState } from "react"
|
||||
import { SlashCommands } from "../extensions/SlashCommands"
|
||||
import { docToTiptap, tiptapToDoc } from "../lib/serialize"
|
||||
import { CardGridBlock, DocumentBlock, EmbedBlock, ImageBlock, TextBlock, WriterDocument } from "../nodes"
|
||||
import { pb } from "../pb"
|
||||
import { DraftBanner } from "./DraftBanner"
|
||||
import { Toolbar } from "./Toolbar"
|
||||
import "../nodes/nodes.css"
|
||||
import "./Editor.css"
|
||||
import type { DocV1, PbPage } from "../types"
|
||||
|
||||
function serializeDoc(doc: DocV1): string {
|
||||
return JSON.stringify(doc)
|
||||
}
|
||||
|
||||
export type EditorProps = {
|
||||
page: PbPage
|
||||
}
|
||||
|
||||
export function Editor({ page }: EditorProps) {
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [publishing, setPublishing] = useState(false)
|
||||
const [markdownMode, setMarkdownMode] = useState(false)
|
||||
const [markdownText, setMarkdownText] = useState("")
|
||||
const [dirty, setDirty] = useState(false)
|
||||
const [hasDraft, setHasDraft] = useState(page.draft !== null)
|
||||
const [publishedDoc, setPublishedDoc] = useState(page.doc)
|
||||
|
||||
const initialDoc = page.draft ?? page.doc
|
||||
const tiptapDoc = docToTiptap(initialDoc)
|
||||
|
||||
const snapshotRef = useRef(serializeDoc(initialDoc))
|
||||
|
||||
const editor = useEditor({
|
||||
extensions: [
|
||||
WriterDocument,
|
||||
StarterKit.configure({
|
||||
document: false,
|
||||
}),
|
||||
TextBlock,
|
||||
Markdown,
|
||||
Link.configure({ openOnClick: false }),
|
||||
ImageBlock,
|
||||
DocumentBlock,
|
||||
EmbedBlock,
|
||||
CardGridBlock,
|
||||
SlashCommands,
|
||||
],
|
||||
content: tiptapDoc,
|
||||
onUpdate: ({ editor }) => {
|
||||
const json = editor.getJSON()
|
||||
const doc = tiptapToDoc(json.content ?? [])
|
||||
const current = serializeDoc(doc)
|
||||
setDirty(current !== snapshotRef.current)
|
||||
},
|
||||
})
|
||||
|
||||
const getCurrentDoc = useCallback((): DocV1 => {
|
||||
if (!editor) return { version: 1, blocks: [] }
|
||||
if (markdownMode) {
|
||||
editor.commands.setContent(markdownText, { contentType: "markdown" })
|
||||
setMarkdownMode(false)
|
||||
}
|
||||
const json = editor.getJSON()
|
||||
return tiptapToDoc(json.content ?? [])
|
||||
}, [editor, markdownMode, markdownText])
|
||||
|
||||
const handleToggleMarkdown = useCallback(() => {
|
||||
if (!editor) return
|
||||
if (!markdownMode) {
|
||||
const md = editor.getMarkdown()
|
||||
setMarkdownText(md)
|
||||
setMarkdownMode(true)
|
||||
} else {
|
||||
editor.commands.setContent(markdownText, { contentType: "markdown" })
|
||||
setMarkdownMode(false)
|
||||
}
|
||||
}, [editor, markdownMode, markdownText])
|
||||
|
||||
const handleSave = useCallback(async () => {
|
||||
if (!editor) return
|
||||
setSaving(true)
|
||||
try {
|
||||
const doc = getCurrentDoc()
|
||||
await pb.collection("pages").update(page.id, { draft: doc })
|
||||
snapshotRef.current = serializeDoc(doc)
|
||||
setDirty(false)
|
||||
setHasDraft(true)
|
||||
} catch (err) {
|
||||
console.error("Save failed:", err)
|
||||
alert("Save failed. Check console.")
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}, [editor, page.id, getCurrentDoc])
|
||||
|
||||
const handlePublish = useCallback(async () => {
|
||||
if (!editor) return
|
||||
setPublishing(true)
|
||||
try {
|
||||
const doc = getCurrentDoc()
|
||||
await pb.collection("pages").update(page.id, { doc, draft: null })
|
||||
snapshotRef.current = serializeDoc(doc)
|
||||
setDirty(false)
|
||||
setHasDraft(false)
|
||||
setPublishedDoc(doc)
|
||||
} catch (err) {
|
||||
console.error("Publish failed:", err)
|
||||
alert("Publish failed. Check console.")
|
||||
} finally {
|
||||
setPublishing(false)
|
||||
}
|
||||
}, [editor, page.id, getCurrentDoc])
|
||||
|
||||
const handleDiscard = useCallback(async () => {
|
||||
if (!editor) return
|
||||
try {
|
||||
await pb.collection("pages").update(page.id, { draft: null })
|
||||
const tiptap = docToTiptap(publishedDoc)
|
||||
editor.commands.setContent(tiptap)
|
||||
snapshotRef.current = serializeDoc(publishedDoc)
|
||||
setDirty(false)
|
||||
setHasDraft(false)
|
||||
setMarkdownMode(false)
|
||||
} catch (err) {
|
||||
console.error("Discard failed:", err)
|
||||
alert("Discard failed. Check console.")
|
||||
}
|
||||
}, [editor, page.id, publishedDoc])
|
||||
|
||||
return (
|
||||
<div className="editor">
|
||||
<Toolbar
|
||||
editor={editor}
|
||||
onSave={handleSave}
|
||||
onPublish={handlePublish}
|
||||
saving={saving}
|
||||
publishing={publishing}
|
||||
dirty={dirty}
|
||||
hasDraft={hasDraft}
|
||||
markdownMode={markdownMode}
|
||||
onToggleMarkdown={handleToggleMarkdown}
|
||||
/>
|
||||
{hasDraft && <DraftBanner onDiscard={handleDiscard} />}
|
||||
{markdownMode ? (
|
||||
<textarea
|
||||
className="editor-markdown"
|
||||
value={markdownText}
|
||||
onChange={e => setMarkdownText(e.target.value)}
|
||||
/>
|
||||
) : (
|
||||
<EditorContent editor={editor} className="editor-content" />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
.file-picker-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.file-picker-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.file-picker-item:hover {
|
||||
background: #f0f4ff;
|
||||
}
|
||||
|
||||
.file-picker-icon {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.file-picker-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
import { pb } from "../pb"
|
||||
import type { PbFile } from "../types"
|
||||
import "./FilePicker.css"
|
||||
|
||||
export type FilePickerProps = {
|
||||
onSelect: (fileId: string) => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export function FilePicker({ onSelect, onClose }: FilePickerProps) {
|
||||
const [files, setFiles] = useState<PbFile[]>([])
|
||||
const [page, setPage] = useState(1)
|
||||
const [totalPages, setTotalPages] = useState(1)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [uploading, setUploading] = useState(false)
|
||||
const [tab, setTab] = useState<"browse" | "upload">("browse")
|
||||
const fileRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
setLoading(true)
|
||||
pb.collection("files")
|
||||
.getList<PbFile>(page, 20, { sort: "-created" })
|
||||
.then(result => {
|
||||
if (cancelled) return
|
||||
setFiles(result.items)
|
||||
setTotalPages(result.totalPages)
|
||||
})
|
||||
.catch(err => {
|
||||
if (!cancelled) console.error("Failed to load files:", err)
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setLoading(false)
|
||||
})
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [page])
|
||||
|
||||
async function handleUpload() {
|
||||
const file = fileRef.current?.files?.[0]
|
||||
if (!file) return
|
||||
setUploading(true)
|
||||
try {
|
||||
const record = await pb.collection("files").create<PbFile>({
|
||||
file,
|
||||
filename: file.name,
|
||||
})
|
||||
onSelect(record.id)
|
||||
} catch (err) {
|
||||
console.error("Upload failed:", err)
|
||||
alert("Upload failed. Check console.")
|
||||
} finally {
|
||||
setUploading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="picker-overlay"
|
||||
onClick={onClose}
|
||||
onKeyDown={e => {
|
||||
if (e.key === "Escape") onClose()
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="picker"
|
||||
onClick={e => e.stopPropagation()}
|
||||
onKeyDown={e => {
|
||||
e.stopPropagation()
|
||||
}}
|
||||
>
|
||||
<div className="picker-header">
|
||||
<button type="button" className={tab === "browse" ? "active" : ""} onClick={() => setTab("browse")}>
|
||||
Browse
|
||||
</button>
|
||||
<button type="button" className={tab === "upload" ? "active" : ""} onClick={() => setTab("upload")}>
|
||||
Upload
|
||||
</button>
|
||||
<button type="button" className="picker-close" onClick={onClose}>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{tab === "browse" && (
|
||||
<div className="picker-browse">
|
||||
{loading ? (
|
||||
<div className="picker-loading">Loading...</div>
|
||||
) : (
|
||||
<div className="file-picker-list">
|
||||
{files.map(f => (
|
||||
<button
|
||||
type="button"
|
||||
key={f.id}
|
||||
className="file-picker-item"
|
||||
onClick={() => onSelect(f.id)}
|
||||
>
|
||||
<span className="file-picker-icon">📎</span>
|
||||
<span className="file-picker-name">{f.filename}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="picker-pagination">
|
||||
<button type="button" disabled={page <= 1} onClick={() => setPage(p => p - 1)}>
|
||||
Prev
|
||||
</button>
|
||||
<span>
|
||||
{page} / {totalPages}
|
||||
</span>
|
||||
<button type="button" disabled={page >= totalPages} onClick={() => setPage(p => p + 1)}>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tab === "upload" && (
|
||||
<div className="picker-upload">
|
||||
<input type="file" ref={fileRef} />
|
||||
<button type="button" onClick={handleUpload} disabled={uploading}>
|
||||
{uploading ? "Uploading..." : "Upload"}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
.picker-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.picker {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
width: 600px;
|
||||
max-width: 90vw;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.picker-header {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.picker-header button {
|
||||
padding: 4px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.picker-header button.active {
|
||||
background: #213547;
|
||||
color: white;
|
||||
border-color: #213547;
|
||||
}
|
||||
|
||||
.picker-close {
|
||||
margin-left: auto !important;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.picker-browse {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.picker-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.picker-thumb {
|
||||
border: 2px solid transparent;
|
||||
border-radius: 4px;
|
||||
padding: 0;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.picker-thumb:hover {
|
||||
border-color: #213547;
|
||||
}
|
||||
|
||||
.picker-thumb img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.picker-pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.picker-pagination button {
|
||||
padding: 4px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.picker-pagination button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.picker-loading {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.picker-upload {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.picker-upload button {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background: #213547;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.picker-upload button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
import { pb } from "../pb"
|
||||
import type { PbImage } from "../types"
|
||||
import "./ImagePicker.css"
|
||||
|
||||
export type ImagePickerProps = {
|
||||
onSelect: (imageId: string) => void
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export function ImagePicker({ onSelect, onClose }: ImagePickerProps) {
|
||||
const [images, setImages] = useState<PbImage[]>([])
|
||||
const [page, setPage] = useState(1)
|
||||
const [totalPages, setTotalPages] = useState(1)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [uploading, setUploading] = useState(false)
|
||||
const [tab, setTab] = useState<"browse" | "upload">("browse")
|
||||
const fileRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
setLoading(true)
|
||||
pb.collection("images")
|
||||
.getList<PbImage>(page, 20, { sort: "-created" })
|
||||
.then(result => {
|
||||
if (cancelled) return
|
||||
setImages(result.items)
|
||||
setTotalPages(result.totalPages)
|
||||
})
|
||||
.catch(err => {
|
||||
if (!cancelled) console.error("Failed to load images:", err)
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setLoading(false)
|
||||
})
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [page])
|
||||
|
||||
async function handleUpload() {
|
||||
const file = fileRef.current?.files?.[0]
|
||||
if (!file) return
|
||||
setUploading(true)
|
||||
try {
|
||||
const record = await pb.collection("images").create<PbImage>({
|
||||
file,
|
||||
filename: file.name,
|
||||
width: 0,
|
||||
height: 0,
|
||||
alt: "",
|
||||
caption: "",
|
||||
blurhash: "",
|
||||
})
|
||||
onSelect(record.id)
|
||||
} catch (err) {
|
||||
console.error("Upload failed:", err)
|
||||
alert("Upload failed. Check console.")
|
||||
} finally {
|
||||
setUploading(false)
|
||||
}
|
||||
}
|
||||
|
||||
function getThumbUrl(img: PbImage): string {
|
||||
return pb.files.getURL(img, img.file, { thumb: "150x150" })
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="picker-overlay"
|
||||
onClick={onClose}
|
||||
onKeyDown={e => {
|
||||
if (e.key === "Escape") onClose()
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="picker"
|
||||
onClick={e => e.stopPropagation()}
|
||||
onKeyDown={e => {
|
||||
e.stopPropagation()
|
||||
}}
|
||||
>
|
||||
<div className="picker-header">
|
||||
<button type="button" className={tab === "browse" ? "active" : ""} onClick={() => setTab("browse")}>
|
||||
Browse
|
||||
</button>
|
||||
<button type="button" className={tab === "upload" ? "active" : ""} onClick={() => setTab("upload")}>
|
||||
Upload
|
||||
</button>
|
||||
<button type="button" className="picker-close" onClick={onClose}>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{tab === "browse" && (
|
||||
<div className="picker-browse">
|
||||
{loading ? (
|
||||
<div className="picker-loading">Loading...</div>
|
||||
) : (
|
||||
<div className="picker-grid">
|
||||
{images.map(img => (
|
||||
<button
|
||||
type="button"
|
||||
key={img.id}
|
||||
className="picker-thumb"
|
||||
onClick={() => onSelect(img.id)}
|
||||
>
|
||||
<img src={getThumbUrl(img)} alt={img.alt || img.filename} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="picker-pagination">
|
||||
<button type="button" disabled={page <= 1} onClick={() => setPage(p => p - 1)}>
|
||||
Prev
|
||||
</button>
|
||||
<span>
|
||||
{page} / {totalPages}
|
||||
</span>
|
||||
<button type="button" disabled={page >= totalPages} onClick={() => setPage(p => p + 1)}>
|
||||
Next
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tab === "upload" && (
|
||||
<div className="picker-upload">
|
||||
<input type="file" accept="image/*" ref={fileRef} />
|
||||
<button type="button" onClick={handleUpload} disabled={uploading}>
|
||||
{uploading ? "Uploading..." : "Upload"}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
.login {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.login-form h1 {
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.login-form input {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.login-form button {
|
||||
padding: 8px 12px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
background: #213547;
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-form button:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.login-error {
|
||||
color: #c00;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import type { FormEvent } from "react"
|
||||
import { useState } from "react"
|
||||
import { pb } from "../pb"
|
||||
import "./Login.css"
|
||||
|
||||
export type LoginProps = {
|
||||
onLogin: () => void
|
||||
}
|
||||
|
||||
export function Login({ onLogin }: LoginProps) {
|
||||
const [email, setEmail] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
const [error, setError] = useState("")
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
async function handleSubmit(e: FormEvent) {
|
||||
e.preventDefault()
|
||||
setError("")
|
||||
setLoading(true)
|
||||
try {
|
||||
await pb.collection("_superusers").authWithPassword(email, password)
|
||||
onLogin()
|
||||
} catch {
|
||||
setError("Authentication failed")
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="login">
|
||||
<form className="login-form" onSubmit={handleSubmit}>
|
||||
<h1>Hudozka Writer</h1>
|
||||
{error && <p className="login-error">{error}</p>}
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={e => setEmail(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
<button type="submit" disabled={loading}>
|
||||
{loading ? "Signing in..." : "Sign in"}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
.toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
margin-bottom: 16px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: white;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.toolbar-group {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.toolbar button {
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
min-width: 32px;
|
||||
}
|
||||
|
||||
.toolbar button:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.toolbar button.active {
|
||||
background: #213547;
|
||||
color: white;
|
||||
border-color: #213547;
|
||||
}
|
||||
|
||||
.toolbar-actions {
|
||||
margin-left: auto;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.toolbar-save-btn {
|
||||
padding: 4px 16px !important;
|
||||
}
|
||||
|
||||
.toolbar-save-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
.toolbar-publish-btn {
|
||||
background: #213547 !important;
|
||||
color: white !important;
|
||||
border-color: #213547 !important;
|
||||
padding: 4px 16px !important;
|
||||
}
|
||||
|
||||
.toolbar-publish-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
import type { Editor } from "@tiptap/react"
|
||||
import "./Toolbar.css"
|
||||
|
||||
export type ToolbarProps = {
|
||||
editor: Editor | null
|
||||
onSave: () => void
|
||||
onPublish: () => void
|
||||
saving: boolean
|
||||
publishing: boolean
|
||||
dirty: boolean
|
||||
hasDraft: boolean
|
||||
markdownMode: boolean
|
||||
onToggleMarkdown: () => void
|
||||
}
|
||||
|
||||
export function Toolbar({
|
||||
editor,
|
||||
onSave,
|
||||
onPublish,
|
||||
saving,
|
||||
publishing,
|
||||
dirty,
|
||||
hasDraft,
|
||||
markdownMode,
|
||||
onToggleMarkdown,
|
||||
}: ToolbarProps) {
|
||||
if (!editor) return null
|
||||
|
||||
const canSave = dirty
|
||||
const canPublish = dirty || hasDraft
|
||||
|
||||
return (
|
||||
<div className="toolbar">
|
||||
<div className="toolbar-group">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||
className={editor.isActive("bold") ? "active" : ""}
|
||||
title="Bold"
|
||||
>
|
||||
B
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||
className={editor.isActive("italic") ? "active" : ""}
|
||||
title="Italic"
|
||||
>
|
||||
I
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editor.chain().focus().toggleStrike().run()}
|
||||
className={editor.isActive("strike") ? "active" : ""}
|
||||
title="Strikethrough"
|
||||
>
|
||||
S
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editor.chain().focus().toggleCode().run()}
|
||||
className={editor.isActive("code") ? "active" : ""}
|
||||
title="Inline code"
|
||||
>
|
||||
</>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="toolbar-group">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
|
||||
className={editor.isActive("heading", { level: 1 }) ? "active" : ""}
|
||||
>
|
||||
H1
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
|
||||
className={editor.isActive("heading", { level: 2 }) ? "active" : ""}
|
||||
>
|
||||
H2
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
|
||||
className={editor.isActive("heading", { level: 3 }) ? "active" : ""}
|
||||
>
|
||||
H3
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="toolbar-group">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editor.chain().focus().toggleBulletList().run()}
|
||||
className={editor.isActive("bulletList") ? "active" : ""}
|
||||
title="Bullet list"
|
||||
>
|
||||
List
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editor.chain().focus().toggleOrderedList().run()}
|
||||
className={editor.isActive("orderedList") ? "active" : ""}
|
||||
title="Numbered list"
|
||||
>
|
||||
1.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editor.chain().focus().toggleBlockquote().run()}
|
||||
className={editor.isActive("blockquote") ? "active" : ""}
|
||||
title="Blockquote"
|
||||
>
|
||||
“
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editor.chain().focus().toggleCodeBlock().run()}
|
||||
className={editor.isActive("codeBlock") ? "active" : ""}
|
||||
title="Code block"
|
||||
>
|
||||
Code
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => editor.chain().focus().setHorizontalRule().run()}
|
||||
title="Horizontal rule"
|
||||
>
|
||||
---
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="toolbar-group">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggleMarkdown}
|
||||
className={markdownMode ? "active" : ""}
|
||||
title="Toggle markdown view"
|
||||
>
|
||||
MD
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="toolbar-group toolbar-actions">
|
||||
<button type="button" onClick={onSave} disabled={!canSave || saving} className="toolbar-save-btn">
|
||||
{saving ? "Saving..." : "Save"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onPublish}
|
||||
disabled={!canPublish || publishing}
|
||||
className="toolbar-publish-btn"
|
||||
>
|
||||
{publishing ? "Publishing..." : "Publish"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
.slash-command-container {
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.slash-menu {
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 4px 0;
|
||||
min-width: 160px;
|
||||
}
|
||||
|
||||
.slash-menu-item {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: none;
|
||||
background: none;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.slash-menu-item:hover,
|
||||
.slash-menu-item--selected {
|
||||
background: #f0f4ff;
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
import { Extension } from "@tiptap/core"
|
||||
import type { Node as PmNode, ResolvedPos } from "@tiptap/pm/model"
|
||||
import { Fragment } from "@tiptap/pm/model"
|
||||
import { PluginKey } from "@tiptap/pm/state"
|
||||
import type { Editor } from "@tiptap/react"
|
||||
import type { SuggestionKeyDownProps, SuggestionOptions, SuggestionProps } from "@tiptap/suggestion"
|
||||
import Suggestion from "@tiptap/suggestion"
|
||||
import { createRoot } from "react-dom/client"
|
||||
import { generateBlockId } from "../lib/id"
|
||||
import "./SlashCommands.css"
|
||||
|
||||
type SlashCommandItem = {
|
||||
label: string
|
||||
type: string
|
||||
attrs?: Record<string, unknown>
|
||||
}
|
||||
|
||||
const ITEMS: SlashCommandItem[] = [
|
||||
{ label: "Text", type: "textBlock" },
|
||||
{ label: "Image", type: "imageBlock", attrs: { id: "", imageId: "", wide: false, caption: "" } },
|
||||
{ label: "File", type: "documentBlock", attrs: { id: "", fileId: "", title: "" } },
|
||||
{ label: "Embed", type: "embedBlock", attrs: { id: "", src: "" } },
|
||||
{ label: "Card Grid", type: "cardGridBlock", attrs: { id: "", items: "[]" } },
|
||||
]
|
||||
|
||||
const slashCommandsPluginKey = new PluginKey("slashCommands")
|
||||
|
||||
function findTextBlockDepth($pos: ResolvedPos): number | null {
|
||||
for (let d = $pos.depth; d > 0; d--) {
|
||||
if ($pos.node(d).type.name === "textBlock") {
|
||||
return d
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function SlashMenu({
|
||||
items,
|
||||
command,
|
||||
selectedIndex,
|
||||
}: {
|
||||
items: SlashCommandItem[]
|
||||
command: (item: SlashCommandItem) => void
|
||||
selectedIndex: number
|
||||
}) {
|
||||
if (items.length === 0) return null
|
||||
|
||||
return (
|
||||
<div className="slash-menu">
|
||||
{items.map((item, index) => (
|
||||
<button
|
||||
type="button"
|
||||
key={item.type}
|
||||
className={`slash-menu-item${index === selectedIndex ? " slash-menu-item--selected" : ""}`}
|
||||
onClick={() => command(item)}
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const SlashCommands = Extension.create({
|
||||
name: "slashCommands",
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
suggestion: {
|
||||
char: "/",
|
||||
pluginKey: slashCommandsPluginKey,
|
||||
startOfLine: true,
|
||||
items: ({ query }: { query: string }) => {
|
||||
return ITEMS.filter(item => item.label.toLowerCase().includes(query.toLowerCase()))
|
||||
},
|
||||
render: () => {
|
||||
let container: HTMLDivElement | null = null
|
||||
let root: ReturnType<typeof createRoot> | null = null
|
||||
let selectedIndex = 0
|
||||
let currentItems: SlashCommandItem[] = []
|
||||
let currentCommand: ((item: SlashCommandItem) => void) | null = null
|
||||
|
||||
function renderMenu() {
|
||||
if (!root || !currentCommand) return
|
||||
root.render(
|
||||
<SlashMenu items={currentItems} command={currentCommand} selectedIndex={selectedIndex} />,
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
onStart: (props: SuggestionProps<SlashCommandItem, SlashCommandItem>) => {
|
||||
container = document.createElement("div")
|
||||
container.className = "slash-command-container"
|
||||
document.body.appendChild(container)
|
||||
root = createRoot(container)
|
||||
|
||||
const rect = props.clientRect?.()
|
||||
if (rect && container) {
|
||||
container.style.position = "fixed"
|
||||
container.style.left = `${rect.left}px`
|
||||
container.style.top = `${rect.bottom + 4}px`
|
||||
container.style.zIndex = "50"
|
||||
}
|
||||
|
||||
selectedIndex = 0
|
||||
currentItems = props.items
|
||||
currentCommand = props.command
|
||||
renderMenu()
|
||||
},
|
||||
onUpdate: (props: SuggestionProps<SlashCommandItem, SlashCommandItem>) => {
|
||||
if (!root || !container) return
|
||||
|
||||
const rect = props.clientRect?.()
|
||||
if (rect && container) {
|
||||
container.style.left = `${rect.left}px`
|
||||
container.style.top = `${rect.bottom + 4}px`
|
||||
}
|
||||
|
||||
currentItems = props.items
|
||||
currentCommand = props.command
|
||||
selectedIndex = 0
|
||||
renderMenu()
|
||||
},
|
||||
onExit: () => {
|
||||
root?.unmount()
|
||||
container?.remove()
|
||||
container = null
|
||||
root = null
|
||||
},
|
||||
onKeyDown: (props: SuggestionKeyDownProps) => {
|
||||
const { key } = props.event
|
||||
if (key === "Escape") {
|
||||
root?.unmount()
|
||||
container?.remove()
|
||||
container = null
|
||||
root = null
|
||||
return true
|
||||
}
|
||||
if (key === "ArrowDown") {
|
||||
selectedIndex = (selectedIndex + 1) % currentItems.length
|
||||
renderMenu()
|
||||
return true
|
||||
}
|
||||
if (key === "ArrowUp") {
|
||||
selectedIndex = (selectedIndex - 1 + currentItems.length) % currentItems.length
|
||||
renderMenu()
|
||||
return true
|
||||
}
|
||||
if (key === "Enter") {
|
||||
if (currentItems[selectedIndex] && currentCommand) {
|
||||
currentCommand(currentItems[selectedIndex])
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
}
|
||||
},
|
||||
command: ({
|
||||
editor,
|
||||
range,
|
||||
props,
|
||||
}: {
|
||||
editor: Editor
|
||||
range: { from: number; to: number }
|
||||
props: SlashCommandItem
|
||||
}) => {
|
||||
// Delete the slash command text
|
||||
editor.chain().focus().deleteRange(range).run()
|
||||
|
||||
if (props.type === "textBlock") {
|
||||
// Already inside a textBlock paragraph — nothing to do
|
||||
return
|
||||
}
|
||||
|
||||
const { from } = editor.state.selection
|
||||
const id = generateBlockId()
|
||||
const newNode = { type: props.type, attrs: { ...props.attrs, id } }
|
||||
|
||||
// Check if we're inside a textBlock
|
||||
const $pos = editor.state.doc.resolve(from)
|
||||
const textBlockDepth = findTextBlockDepth($pos)
|
||||
|
||||
if (textBlockDepth === null) {
|
||||
// Not inside a textBlock — insert directly
|
||||
editor.chain().focus().insertContentAt(from, newNode).run()
|
||||
return
|
||||
}
|
||||
|
||||
// Inside a textBlock — split it and insert the custom block between halves
|
||||
const textBlockPos = $pos.before(textBlockDepth)
|
||||
const textBlockNode = editor.state.doc.nodeAt(textBlockPos)
|
||||
if (!textBlockNode) return
|
||||
|
||||
// Find which child contains the cursor
|
||||
let childOffset = textBlockPos + 1
|
||||
let splitAfter = -1
|
||||
for (let i = 0; i < textBlockNode.childCount; i++) {
|
||||
const child = textBlockNode.child(i)
|
||||
const childEnd = childOffset + child.nodeSize
|
||||
if (from >= childOffset && from <= childEnd) {
|
||||
// If the paragraph is empty (just deleted the slash), split before it
|
||||
const curChild = textBlockNode.child(i)
|
||||
if (curChild.content.size === 0 && i > 0) {
|
||||
splitAfter = i - 1
|
||||
} else if (curChild.content.size === 0 && i === 0) {
|
||||
splitAfter = -1 // cursor in empty first child
|
||||
} else {
|
||||
splitAfter = i
|
||||
}
|
||||
break
|
||||
}
|
||||
childOffset = childEnd
|
||||
}
|
||||
|
||||
const { schema } = editor.state
|
||||
const textBlockType = schema.nodes.textBlock
|
||||
const fragments: PmNode[] = []
|
||||
|
||||
// Build first textBlock (children 0..splitAfter) — skip if empty
|
||||
if (splitAfter >= 0) {
|
||||
const children = []
|
||||
for (let i = 0; i <= splitAfter; i++) {
|
||||
children.push(textBlockNode.child(i))
|
||||
}
|
||||
// Skip if all children are empty paragraphs
|
||||
const hasContent = children.some(c => c.content.size > 0)
|
||||
if (hasContent) {
|
||||
fragments.push(textBlockType.create(null, children))
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the custom block
|
||||
fragments.push(schema.nodes[props.type].create({ ...props.attrs, id }))
|
||||
|
||||
// Build second textBlock (children after splitAfter) — skip empty
|
||||
const remaining = []
|
||||
const startIdx = splitAfter + 1
|
||||
for (let i = startIdx; i < textBlockNode.childCount; i++) {
|
||||
remaining.push(textBlockNode.child(i))
|
||||
}
|
||||
// Filter out empty paragraphs at the edges
|
||||
const nonEmpty = remaining.filter(c => c.content.size > 0)
|
||||
if (nonEmpty.length > 0) {
|
||||
fragments.push(textBlockType.create(null, remaining))
|
||||
}
|
||||
|
||||
const tr = editor.state.tr
|
||||
tr.replaceWith(textBlockPos, textBlockPos + textBlockNode.nodeSize, Fragment.from(fragments))
|
||||
editor.view.dispatch(tr)
|
||||
},
|
||||
} satisfies Partial<SuggestionOptions<SlashCommandItem, SlashCommandItem>>,
|
||||
}
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
Suggestion({
|
||||
editor: this.editor,
|
||||
...this.options.suggestion,
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
@@ -1,16 +0,0 @@
|
||||
:root {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
line-height: 1.5;
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export function generateBlockId(): string {
|
||||
return crypto.randomUUID().slice(0, 8)
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import Link from "@tiptap/extension-link"
|
||||
import { MarkdownManager } from "@tiptap/markdown"
|
||||
import StarterKit from "@tiptap/starter-kit"
|
||||
|
||||
let manager: MarkdownManager | null = null
|
||||
|
||||
export function getMarkdownManager(): MarkdownManager {
|
||||
if (!manager) {
|
||||
manager = new MarkdownManager({
|
||||
extensions: [StarterKit, Link],
|
||||
})
|
||||
}
|
||||
return manager
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
import type { JSONContent } from "@tiptap/core"
|
||||
import type {
|
||||
DocV1,
|
||||
DocV1Block,
|
||||
DocV1BlockCardGrid,
|
||||
DocV1BlockDocument,
|
||||
DocV1BlockEmbed,
|
||||
DocV1BlockImage,
|
||||
DocV1BlockText,
|
||||
} from "../types"
|
||||
import { generateBlockId } from "./id"
|
||||
import { getMarkdownManager } from "./markdown"
|
||||
|
||||
// -- DocV1 -> Tiptap JSON --
|
||||
|
||||
function textBlockToTiptap(block: DocV1BlockText): JSONContent {
|
||||
const manager = getMarkdownManager()
|
||||
const parsed = manager.parse(block.text)
|
||||
return {
|
||||
type: "textBlock",
|
||||
content: parsed.content ?? [],
|
||||
}
|
||||
}
|
||||
|
||||
function imageBlockToTiptap(block: DocV1BlockImage): JSONContent {
|
||||
return {
|
||||
type: "imageBlock",
|
||||
attrs: {
|
||||
id: block.id,
|
||||
imageId: block.image,
|
||||
wide: block.wide,
|
||||
caption: block.caption,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function documentBlockToTiptap(block: DocV1BlockDocument): JSONContent {
|
||||
return {
|
||||
type: "documentBlock",
|
||||
attrs: {
|
||||
id: block.id,
|
||||
fileId: block.file,
|
||||
title: block.title,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function embedBlockToTiptap(block: DocV1BlockEmbed): JSONContent {
|
||||
return {
|
||||
type: "embedBlock",
|
||||
attrs: {
|
||||
id: block.id,
|
||||
src: block.src,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function cardGridBlockToTiptap(block: DocV1BlockCardGrid): JSONContent {
|
||||
return {
|
||||
type: "cardGridBlock",
|
||||
attrs: {
|
||||
id: block.id,
|
||||
items: JSON.stringify(block.items),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function docToTiptap(doc: DocV1): JSONContent {
|
||||
const content: JSONContent[] = []
|
||||
|
||||
for (const block of doc.blocks) {
|
||||
switch (block.type) {
|
||||
case "text":
|
||||
content.push(textBlockToTiptap(block))
|
||||
break
|
||||
case "image":
|
||||
content.push(imageBlockToTiptap(block))
|
||||
break
|
||||
case "document":
|
||||
content.push(documentBlockToTiptap(block))
|
||||
break
|
||||
case "embed":
|
||||
content.push(embedBlockToTiptap(block))
|
||||
break
|
||||
case "card-grid":
|
||||
content.push(cardGridBlockToTiptap(block))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: "doc",
|
||||
content,
|
||||
}
|
||||
}
|
||||
|
||||
// -- Tiptap JSON -> DocV1 --
|
||||
|
||||
function textNodesToMarkdown(nodes: JSONContent[]): string {
|
||||
const manager = getMarkdownManager()
|
||||
const doc: JSONContent = { type: "doc", content: nodes }
|
||||
return manager.serialize(doc)
|
||||
}
|
||||
|
||||
export function tiptapToDoc(content: JSONContent[]): DocV1 {
|
||||
const blocks: DocV1Block[] = []
|
||||
|
||||
for (const node of content) {
|
||||
switch (node.type) {
|
||||
case "textBlock": {
|
||||
const text = textNodesToMarkdown(node.content ?? [])
|
||||
if (text.trim()) {
|
||||
blocks.push({
|
||||
type: "text",
|
||||
id: generateBlockId(),
|
||||
text,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case "imageBlock": {
|
||||
const attrs = node.attrs ?? {}
|
||||
blocks.push({
|
||||
type: "image",
|
||||
id: attrs.id ?? generateBlockId(),
|
||||
image: attrs.imageId ?? "",
|
||||
wide: attrs.wide ?? false,
|
||||
caption: attrs.caption ?? "",
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case "documentBlock": {
|
||||
const attrs = node.attrs ?? {}
|
||||
blocks.push({
|
||||
type: "document",
|
||||
id: attrs.id ?? generateBlockId(),
|
||||
file: attrs.fileId ?? "",
|
||||
title: attrs.title ?? "",
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case "embedBlock": {
|
||||
const attrs = node.attrs ?? {}
|
||||
blocks.push({
|
||||
type: "embed",
|
||||
id: attrs.id ?? generateBlockId(),
|
||||
src: attrs.src ?? "",
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case "cardGridBlock": {
|
||||
const attrs = node.attrs ?? {}
|
||||
blocks.push({
|
||||
type: "card-grid",
|
||||
id: attrs.id ?? generateBlockId(),
|
||||
items: JSON.parse(attrs.items ?? "[]"),
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { version: 1, blocks }
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { StrictMode } from "react"
|
||||
import { createRoot } from "react-dom/client"
|
||||
import { App } from "./App"
|
||||
import "./index.css"
|
||||
|
||||
const rootElement = document.getElementById("root")
|
||||
if (!rootElement) throw new Error("Root element #root not found")
|
||||
|
||||
createRoot(rootElement).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
@@ -1,90 +0,0 @@
|
||||
import { mergeAttributes, Node } from "@tiptap/core"
|
||||
import type { NodeViewProps } from "@tiptap/react"
|
||||
import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react"
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import { BlockActions } from "../components/BlockActions"
|
||||
import { BlockInsert } from "../components/BlockInsert"
|
||||
import { pb } from "../pb"
|
||||
import type { PbPage } from "../types"
|
||||
|
||||
type ResolvedItem = {
|
||||
page: string
|
||||
title: string | null
|
||||
}
|
||||
|
||||
function CardGridBlockView({ node, editor, getPos }: NodeViewProps) {
|
||||
const items = useMemo<Array<{ page: string }>>(() => JSON.parse(node.attrs.items || "[]"), [node.attrs.items])
|
||||
const [resolved, setResolved] = useState<ResolvedItem[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (items.length === 0) {
|
||||
setResolved([])
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
Promise.all(
|
||||
items.map(async item => {
|
||||
try {
|
||||
const page = await pb.collection("pages").getOne<PbPage>(item.page)
|
||||
return { page: item.page, title: page.title }
|
||||
} catch {
|
||||
return { page: item.page, title: null }
|
||||
}
|
||||
}),
|
||||
).then(results => {
|
||||
setResolved(results)
|
||||
setLoading(false)
|
||||
})
|
||||
}, [items])
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeViewWrapper className="node-view-block node-readonly-block">
|
||||
<BlockActions editor={editor} getPos={getPos} />
|
||||
<div className="node-view-content">
|
||||
<div className="node-block-label">Card Grid ({items.length})</div>
|
||||
{loading ? (
|
||||
<div className="node-block-info">Loading...</div>
|
||||
) : (
|
||||
<ul className="node-cardgrid-list">
|
||||
{resolved.map(item => (
|
||||
<li key={item.page} className="node-cardgrid-item">
|
||||
{item.title ?? item.page}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</NodeViewWrapper>
|
||||
<BlockInsert editor={editor} getPos={getPos} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const CardGridBlock = Node.create({
|
||||
name: "cardGridBlock",
|
||||
group: "block",
|
||||
atom: true,
|
||||
draggable: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
id: { default: null },
|
||||
items: { default: "[]" },
|
||||
}
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [{ tag: "div[data-card-grid-block]" }]
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return ["div", mergeAttributes(HTMLAttributes, { "data-card-grid-block": "" })]
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return ReactNodeViewRenderer(CardGridBlockView)
|
||||
},
|
||||
})
|
||||
@@ -1,103 +0,0 @@
|
||||
import { mergeAttributes, Node } from "@tiptap/core"
|
||||
import type { NodeViewProps } from "@tiptap/react"
|
||||
import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { BlockActions } from "../components/BlockActions"
|
||||
import { BlockInsert } from "../components/BlockInsert"
|
||||
import { FilePicker } from "../components/FilePicker"
|
||||
import { pb } from "../pb"
|
||||
import type { PbFile } from "../types"
|
||||
|
||||
function DocumentBlockView({ node, updateAttributes, editor, getPos }: NodeViewProps) {
|
||||
const { fileId, title } = node.attrs
|
||||
const [filename, setFilename] = useState<string | null>(null)
|
||||
const [showPicker, setShowPicker] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!fileId) return
|
||||
pb.collection("files")
|
||||
.getOne<PbFile>(fileId)
|
||||
.then(f => {
|
||||
setFilename(f.filename)
|
||||
})
|
||||
.catch(() => {
|
||||
setFilename(null)
|
||||
})
|
||||
}, [fileId])
|
||||
|
||||
function handleSelect(newFileId: string) {
|
||||
updateAttributes({ fileId: newFileId })
|
||||
setShowPicker(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeViewWrapper className="node-view-block node-document-block">
|
||||
<BlockActions editor={editor} getPos={getPos} />
|
||||
<div className="node-view-content">
|
||||
<div className="node-block-label">File</div>
|
||||
{fileId ? (
|
||||
<button
|
||||
type="button"
|
||||
className="node-document-file"
|
||||
onClick={() => setShowPicker(true)}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
📎 {filename ?? fileId}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="node-document-placeholder"
|
||||
onClick={() => setShowPicker(true)}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
Click to select file
|
||||
</button>
|
||||
)}
|
||||
<div className="node-document-controls">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Title"
|
||||
value={title || ""}
|
||||
onChange={e => updateAttributes({ title: e.target.value })}
|
||||
className="node-document-title"
|
||||
/>
|
||||
<button type="button" className="node-image-pick-btn" onClick={() => setShowPicker(true)}>
|
||||
Pick
|
||||
</button>
|
||||
</div>
|
||||
{showPicker && <FilePicker onSelect={handleSelect} onClose={() => setShowPicker(false)} />}
|
||||
</div>
|
||||
</NodeViewWrapper>
|
||||
<BlockInsert editor={editor} getPos={getPos} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const DocumentBlock = Node.create({
|
||||
name: "documentBlock",
|
||||
group: "block",
|
||||
atom: true,
|
||||
draggable: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
id: { default: null },
|
||||
fileId: { default: "" },
|
||||
title: { default: "" },
|
||||
}
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [{ tag: "div[data-document-block]" }]
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return ["div", mergeAttributes(HTMLAttributes, { "data-document-block": "" })]
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return ReactNodeViewRenderer(DocumentBlockView)
|
||||
},
|
||||
})
|
||||
@@ -1,48 +0,0 @@
|
||||
import { mergeAttributes, Node } from "@tiptap/core"
|
||||
import type { NodeViewProps } from "@tiptap/react"
|
||||
import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react"
|
||||
import { BlockActions } from "../components/BlockActions"
|
||||
import { BlockInsert } from "../components/BlockInsert"
|
||||
|
||||
function EmbedBlockView({ node, editor, getPos }: NodeViewProps) {
|
||||
return (
|
||||
<>
|
||||
<NodeViewWrapper className="node-view-block node-readonly-block">
|
||||
<BlockActions editor={editor} getPos={getPos} />
|
||||
<div className="node-view-content">
|
||||
<div className="node-block-label">Embed</div>
|
||||
<div className="node-block-info">
|
||||
<code>{node.attrs.src}</code>
|
||||
</div>
|
||||
</div>
|
||||
</NodeViewWrapper>
|
||||
<BlockInsert editor={editor} getPos={getPos} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const EmbedBlock = Node.create({
|
||||
name: "embedBlock",
|
||||
group: "block",
|
||||
atom: true,
|
||||
draggable: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
id: { default: null },
|
||||
src: { default: "" },
|
||||
}
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [{ tag: "div[data-embed-block]" }]
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return ["div", mergeAttributes(HTMLAttributes, { "data-embed-block": "" })]
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return ReactNodeViewRenderer(EmbedBlockView)
|
||||
},
|
||||
})
|
||||
@@ -1,113 +0,0 @@
|
||||
import { mergeAttributes, Node } from "@tiptap/core"
|
||||
import type { NodeViewProps } from "@tiptap/react"
|
||||
import { NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { BlockActions } from "../components/BlockActions"
|
||||
import { BlockInsert } from "../components/BlockInsert"
|
||||
import { ImagePicker } from "../components/ImagePicker"
|
||||
import { pb } from "../pb"
|
||||
import type { PbImage } from "../types"
|
||||
|
||||
function ImageBlockView({ node, updateAttributes, editor, getPos }: NodeViewProps) {
|
||||
const { imageId, wide, caption } = node.attrs
|
||||
const [thumbnail, setThumbnail] = useState<string | null>(null)
|
||||
const [showPicker, setShowPicker] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!imageId) return
|
||||
pb.collection("images")
|
||||
.getOne<PbImage>(imageId)
|
||||
.then(img => {
|
||||
const url = pb.files.getURL(img, img.file, { thumb: "300x200" })
|
||||
setThumbnail(url)
|
||||
})
|
||||
.catch(() => {
|
||||
setThumbnail(null)
|
||||
})
|
||||
}, [imageId])
|
||||
|
||||
function handleSelect(newImageId: string) {
|
||||
updateAttributes({ imageId: newImageId })
|
||||
setShowPicker(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<NodeViewWrapper className="node-view-block node-image-block">
|
||||
<BlockActions editor={editor} getPos={getPos} />
|
||||
<div className="node-view-content">
|
||||
<div className="node-block-label">Image</div>
|
||||
{thumbnail ? (
|
||||
<button
|
||||
type="button"
|
||||
className="node-image-thumb-button"
|
||||
onClick={() => setShowPicker(true)}
|
||||
style={{ cursor: "pointer", padding: 0, border: 0, background: "none" }}
|
||||
>
|
||||
<img src={thumbnail} alt={caption || "image"} className="node-image-thumb" />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="node-image-placeholder"
|
||||
onClick={() => setShowPicker(true)}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
{imageId ? "Loading..." : "Click to select image"}
|
||||
</button>
|
||||
)}
|
||||
<div className="node-image-controls">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={wide}
|
||||
onChange={e => updateAttributes({ wide: e.target.checked })}
|
||||
/>
|
||||
Wide
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Caption"
|
||||
value={caption || ""}
|
||||
onChange={e => updateAttributes({ caption: e.target.value })}
|
||||
className="node-image-caption"
|
||||
/>
|
||||
<button type="button" className="node-image-pick-btn" onClick={() => setShowPicker(true)}>
|
||||
Pick
|
||||
</button>
|
||||
</div>
|
||||
{showPicker && <ImagePicker onSelect={handleSelect} onClose={() => setShowPicker(false)} />}
|
||||
</div>
|
||||
</NodeViewWrapper>
|
||||
<BlockInsert editor={editor} getPos={getPos} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const ImageBlock = Node.create({
|
||||
name: "imageBlock",
|
||||
group: "block",
|
||||
atom: true,
|
||||
draggable: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
id: { default: null },
|
||||
imageId: { default: "" },
|
||||
wide: { default: false },
|
||||
caption: { default: "" },
|
||||
}
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [{ tag: "div[data-image-block]" }]
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return ["div", mergeAttributes(HTMLAttributes, { "data-image-block": "" })]
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return ReactNodeViewRenderer(ImageBlockView)
|
||||
},
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
import { mergeAttributes, Node } from "@tiptap/core"
|
||||
import type { NodeViewProps } from "@tiptap/react"
|
||||
import { NodeViewContent, NodeViewWrapper, ReactNodeViewRenderer } from "@tiptap/react"
|
||||
import { BlockActions } from "../components/BlockActions"
|
||||
import { BlockInsert } from "../components/BlockInsert"
|
||||
|
||||
function TextBlockView({ editor, getPos }: NodeViewProps) {
|
||||
return (
|
||||
<>
|
||||
<NodeViewWrapper className="node-view-block node-text-block">
|
||||
<BlockActions editor={editor} getPos={getPos} />
|
||||
<NodeViewContent className="node-view-content" />
|
||||
</NodeViewWrapper>
|
||||
<BlockInsert editor={editor} getPos={getPos} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const TextBlock = Node.create({
|
||||
name: "textBlock",
|
||||
group: "block",
|
||||
content: "(paragraph | heading | bulletList | orderedList | blockquote | codeBlock | horizontalRule)+",
|
||||
draggable: true,
|
||||
defining: true,
|
||||
|
||||
parseHTML() {
|
||||
return [{ tag: "div[data-text-block]" }]
|
||||
},
|
||||
|
||||
renderHTML({ HTMLAttributes }) {
|
||||
return ["div", mergeAttributes(HTMLAttributes, { "data-text-block": "" }), 0]
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return ReactNodeViewRenderer(TextBlockView)
|
||||
},
|
||||
})
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Node } from "@tiptap/core"
|
||||
|
||||
export const WriterDocument = Node.create({
|
||||
name: "doc",
|
||||
topNode: true,
|
||||
content: "(textBlock | imageBlock | documentBlock | embedBlock | cardGridBlock)+",
|
||||
})
|
||||
@@ -1,6 +0,0 @@
|
||||
export { CardGridBlock } from "./CardGridBlock"
|
||||
export { DocumentBlock } from "./DocumentBlock"
|
||||
export { EmbedBlock } from "./EmbedBlock"
|
||||
export { ImageBlock } from "./ImageBlock"
|
||||
export { TextBlock } from "./TextBlock"
|
||||
export { WriterDocument } from "./WriterDocument"
|
||||
@@ -1,109 +0,0 @@
|
||||
.node-block-label {
|
||||
font-size: 0.7rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: #888;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.node-image-block > .node-view-content,
|
||||
.node-readonly-block > .node-view-content,
|
||||
.node-document-block > .node-view-content {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.node-image-thumb {
|
||||
max-width: 100%;
|
||||
max-height: 200px;
|
||||
border-radius: 4px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.node-image-placeholder {
|
||||
height: 100px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.node-image-controls {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.node-image-caption {
|
||||
flex: 1;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.node-block-info {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.node-block-info code {
|
||||
font-size: 0.8rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.node-image-pick-btn {
|
||||
padding: 2px 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.node-document-file {
|
||||
padding: 8px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.node-document-placeholder {
|
||||
padding: 8px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
color: #888;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.node-document-controls {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.node-document-title {
|
||||
flex: 1;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.node-cardgrid-list {
|
||||
margin: 4px 0 0;
|
||||
padding-left: 20px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.node-cardgrid-item {
|
||||
color: #444;
|
||||
line-height: 1.6;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import PocketBase from "pocketbase"
|
||||
|
||||
// Default to same origin; override via env for dev
|
||||
const url = import.meta.env.VITE_PB_URL ?? "http://127.0.0.1:8090"
|
||||
|
||||
export const pb = new PocketBase(url)
|
||||
pb.autoCancellation(false)
|
||||
@@ -1,85 +0,0 @@
|
||||
// -- PocketBase record base --
|
||||
|
||||
export type PbRecord = {
|
||||
id: string
|
||||
collectionId: string
|
||||
collectionName: string
|
||||
created: string
|
||||
updated: string
|
||||
}
|
||||
|
||||
// -- PocketBase collection types --
|
||||
|
||||
export type PbImage = PbRecord & {
|
||||
file: string
|
||||
filename: string
|
||||
width: number
|
||||
height: number
|
||||
blurhash: string
|
||||
alt: string
|
||||
caption: string
|
||||
}
|
||||
|
||||
export type PbFile = PbRecord & {
|
||||
file: string
|
||||
filename: string
|
||||
}
|
||||
|
||||
export type PbPage = PbRecord & {
|
||||
title: string
|
||||
slug: string
|
||||
excerpt: string
|
||||
date: string
|
||||
cover: string
|
||||
doc: DocV1
|
||||
tags: string[]
|
||||
published: boolean
|
||||
draft: DocV1 | null
|
||||
}
|
||||
|
||||
// -- DocV1 document format --
|
||||
|
||||
export type DocV1BlockText = {
|
||||
type: "text"
|
||||
id: string
|
||||
text: string
|
||||
}
|
||||
|
||||
export type DocV1BlockImage = {
|
||||
type: "image"
|
||||
id: string
|
||||
image: string
|
||||
wide: boolean
|
||||
caption: string
|
||||
}
|
||||
|
||||
export type DocV1BlockDocument = {
|
||||
type: "document"
|
||||
id: string
|
||||
file: string
|
||||
title: string
|
||||
}
|
||||
|
||||
export type DocV1BlockEmbed = {
|
||||
type: "embed"
|
||||
id: string
|
||||
src: string
|
||||
}
|
||||
|
||||
export type DocV1BlockCardGridItem = {
|
||||
page: string
|
||||
layout: "small" | "medium" | "big"
|
||||
}
|
||||
|
||||
export type DocV1BlockCardGrid = {
|
||||
type: "card-grid"
|
||||
id: string
|
||||
items: DocV1BlockCardGridItem[]
|
||||
}
|
||||
|
||||
export type DocV1Block = DocV1BlockText | DocV1BlockImage | DocV1BlockDocument | DocV1BlockEmbed | DocV1BlockCardGrid
|
||||
|
||||
export type DocV1 = {
|
||||
version: 1
|
||||
blocks: DocV1Block[]
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src", "vite.config.ts"]
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { defineConfig } from "vite"
|
||||
import react from "@vitejs/plugin-react"
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
@@ -1,5 +0,0 @@
|
||||
modules
|
||||
.vercel
|
||||
.next
|
||||
.github
|
||||
node_modules
|
||||
@@ -1,3 +0,0 @@
|
||||
APP_CARD_DEFAULT_IMAGE=https://art.shlisselburg.org/static/img/main.jpg
|
||||
NEXT_PUBLIC_YMETRIKA_ACCOUNT=XXX
|
||||
POCKETBASE_URL=http://127.0.0.1:8090
|
||||
@@ -1 +0,0 @@
|
||||
POCKETBASE_URL=http://127.0.0.1:8090
|
||||
@@ -1,59 +0,0 @@
|
||||
FROM node:24.12.0-alpine AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
# Copy workspace manifests (root + every workspace) to leverage npm ci caching.
|
||||
COPY package.json package-lock.json ./
|
||||
COPY apps/hudozka/package.json ./apps/hudozka/
|
||||
COPY apps/hudozka-writer/package.json ./apps/hudozka-writer/
|
||||
COPY apps/migrate-to-pocketbase/package.json ./apps/migrate-to-pocketbase/
|
||||
COPY packages/ ./packages/
|
||||
|
||||
RUN npm ci
|
||||
RUN npm install --os=linux --libc=musl --cpu=x64 sharp --workspace apps/hudozka
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
RUN npm run build -w apps/hudozka
|
||||
|
||||
# Production image, copy all the files and run next
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/hudozka/public ./apps/hudozka/public
|
||||
|
||||
# Set the correct permission for prerender cache
|
||||
RUN mkdir -p apps/hudozka/.next
|
||||
RUN chown nextjs:nodejs apps/hudozka/.next
|
||||
|
||||
# Automatically leverage output traces to reduce image size
|
||||
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/hudozka/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/hudozka/.next/static ./apps/hudozka/.next/static
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
ENV PORT=3000
|
||||
|
||||
# server.js is created by next build from the standalone output
|
||||
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
|
||||
ENV HOSTNAME="0.0.0.0"
|
||||
WORKDIR /app/apps/hudozka
|
||||
CMD ["node", "server.js"]
|
||||
@@ -1,6 +0,0 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
@@ -1,18 +0,0 @@
|
||||
import type { NextConfig } from "next"
|
||||
import path from "node:path"
|
||||
|
||||
const config: NextConfig = {
|
||||
output: "standalone",
|
||||
outputFileTracingRoot: path.join(process.cwd(), "../../"),
|
||||
trailingSlash: false,
|
||||
images: {
|
||||
unoptimized: true,
|
||||
remotePatterns: [
|
||||
{ hostname: "hudozka.shlisselburg.org" },
|
||||
{ hostname: "images.weserv.nl" },
|
||||
{ hostname: "127.0.0.1" },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"name": "hudozka",
|
||||
"private": true,
|
||||
"version": "7.0.0",
|
||||
"type": "module",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "next start",
|
||||
"dev": "next dev --turbo",
|
||||
"build": "next build",
|
||||
"lint": "biome check",
|
||||
"format": "biome check --write",
|
||||
"test": "vitest run"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tmshv/hudozka"
|
||||
},
|
||||
"author": "Roman Timashev",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@hudozka/hooks": "*",
|
||||
"@hudozka/text": "*",
|
||||
"@hudozka/ui": "*",
|
||||
"@hudozka/utils": "*",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.0",
|
||||
"@radix-ui/react-tooltip": "^1.2.0",
|
||||
"classnames": "^2.5.0",
|
||||
"dangerously-set-html-content": "^1.1.0",
|
||||
"next": "^16.3.0",
|
||||
"pocketbase": "^0.26.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"sharp": "^0.34.0",
|
||||
"valtio": "^2.3.0"
|
||||
},
|
||||
"overrides": {
|
||||
"dangerously-set-html-content": {
|
||||
"react": "$react"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 3.1 MiB |
|
Before Width: | Height: | Size: 6.2 MiB |
|
Before Width: | Height: | Size: 772 KiB |
@@ -1,713 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg>
|
||||
<metadata>
|
||||
Created by FontForge 20090622 at Wed May 4 16:42:32 2016
|
||||
By deploy user
|
||||
</metadata>
|
||||
<defs>
|
||||
<font id="Shadow-Regular" horiz-adv-x="729" >
|
||||
<font-face
|
||||
font-family="Shadow"
|
||||
font-weight="400"
|
||||
font-stretch="normal"
|
||||
units-per-em="1000"
|
||||
panose-1="0 0 0 0 0 0 0 0 0 0"
|
||||
ascent="750"
|
||||
descent="-250"
|
||||
x-height="635"
|
||||
cap-height="638.5"
|
||||
bbox="-64.1538 -251 1003 860"
|
||||
underline-thickness="50"
|
||||
underline-position="-50"
|
||||
unicode-range="U+000D-U+20B4"
|
||||
/>
|
||||
<missing-glyph horiz-adv-x="500"
|
||||
/>
|
||||
<glyph glyph-name=".notdef" horiz-adv-x="500"
|
||||
/>
|
||||
<glyph glyph-name="NULL" horiz-adv-x="0"
|
||||
/>
|
||||
<glyph glyph-name="NULL" horiz-adv-x="0"
|
||||
/>
|
||||
<glyph glyph-name="nonmarkingreturn" unicode="
" horiz-adv-x="500"
|
||||
/>
|
||||
<glyph glyph-name="space" unicode=" " horiz-adv-x="323"
|
||||
/>
|
||||
<glyph glyph-name="uni0418" unicode="И" horiz-adv-x="750"
|
||||
d="M7 616q0 18 4.5 21.5t23.5 1.5q20 -2 40.5 -1.5t40.5 -0.5q12 0 24.5 1t25.5 -1q15 -2 20.5 -8.5t5.5 -22.5q1 -5 0.5 -9.5t-0.5 -9.5v-264v-13.5t1 -13.5q0 -7 5 -9q6 0 9 5q4 3 7 7t5 8q51 71 100.5 148.5t99.5 147.5q12 20 28 28.5t43 7.5q26 -3 53.5 -2t53.5 0
|
||||
q24 -2 31 -9t6 -31q-2 -102 -1.5 -209.5t-1.5 -209.5q-1 -39 0 -73.5t2 -73.5q0 -7 0.5 -12t-0.5 -12q-1 -9 -6 -14.5t-15 -3.5q-32 1 -65 2.5t-66 0.5q-7 0 -14 0.5t-13 3.5q-4 2 -7.5 7t-3.5 10q2 37 0 80t0 80q1 37 1 73.5t1 73.5q0 2 -1 6t-5 4q-3 0 -4.5 -2t-3.5 -4
|
||||
q-48 -59 -96 -127.5t-88 -134.5l-15.5 -24l-15.5 -24q-9 -15 -22 -19t-29 -6q-12 -1 -23.5 -0.5t-23.5 0.5q-20 1 -40.5 -0.5t-40.5 0.5q-23 2 -27.5 8t-3.5 30q2 37 2.5 73.5t0.5 73.5q0 38 -1 75.5l-2 75.5l0.5 15t0.5 39v55v63.5v63.5v55z" />
|
||||
<glyph glyph-name="uni0413" unicode="Г" horiz-adv-x="542"
|
||||
d="M8 293v50t-0.5 61l-1 64t-1 58.5t-1 44.5t-0.5 23v15t1 15q0 17 19 18q21 0 54.5 0.5t73 0.5h81h77.5t63.5 -0.5t38.5 -0.5q18 0 22 -4t6 -22q3 -31 2 -58.5t2 -58.5q2 -16 -2 -21.5t-19 -7.5q-6 -1 -33 -1.5t-61.5 -0.5t-69 -0.5t-56.5 -0.5q-16 0 -16 -17
|
||||
q0 -4 -0.5 -7.5t-0.5 -7.5q1 -75 0 -156t1 -156q1 -23 0.5 -40t0.5 -40q1 -14 -2 -31q-3 -15 -19 -15q-38 -2 -72.5 -2.5t-71.5 -1.5q-8 0 -10.5 4t-3.5 10q-1 7 -1 13.5v13.5v130t-1 129h1z" />
|
||||
<glyph glyph-name="uni041F" unicode="П" horiz-adv-x="688"
|
||||
d="M561 353q-2 -58 -3 -115l-2 -114q-1 -26 0.5 -47.5t2.5 -47.5q0 -15 -3.5 -19.5t-17.5 -5.5l-65.5 -4t-65.5 -3q-19 -1 -19 23q0 38 0.5 70.5t0.5 70.5q0 63 0.5 124.5t-0.5 123.5q0 27 -1 39t-6 16.5t-17 4t-34 0.5q-29 1 -58 1.5t-58 -0.5q-19 0 -23.5 -4.5t-4.5 -21.5
|
||||
v-175q1 -58 1.5 -116t1.5 -116q0 -5 -1 -8q-1 -13 -2 -20t-5 -10t-11 -3.5t-20 -0.5q-28 1 -56 1.5t-56 3.5q-22 2 -27 8.5t-5 28.5v450v133q0 14 3 16t18 1q48 -2 94.5 -1.5t94.5 0.5q66 -1 129 2t127 0q16 0 31 -0.5t31 -0.5q15 0 18.5 -3t3.5 -18v-93q0 -21 0.5 -50
|
||||
l1 -56t1.5 -45.5z" />
|
||||
<glyph glyph-name="uni0422" unicode="Т" horiz-adv-x="614"
|
||||
d="M468 637q7 -1 13.5 -1t13.5 -2q20 -3 25 -8t5 -26q0 -26 -0.5 -47t-1.5 -47q-1 -17 -5 -21t-20 -3q-12 0 -30.5 -0.5t-36.5 -0.5t-32.5 -0.5t-17.5 -0.5q-15 0 -23.5 -1t-12 -5t-4 -12t0.5 -23v-32.5t-1 -75.5t-1.5 -97.5t-1.5 -97.5t-1.5 -75t-0.5 -31q-3 -30 -32 -31
|
||||
h-21.5h-26t-22.5 0.5t-12 0.5q-16 0 -25 0.5t-13 4.5t-5 12.5t-1 24.5v33t0.5 60t1 77l1 83t0.5 77.5v61.5v27q1 17 -19 19q-13 0 -34 1t-41.5 1.5t-37 1.5t-19.5 1q-15 1 -20 6t-5 20v39t1 39v12q0 9 -1 19t4 16q3 5 13.5 3.5t19.5 -1.5q13 0 46.5 0.5t76.5 0.5t91 -0.5
|
||||
t91 -0.5t76 -0.5t45 -0.5z" />
|
||||
<glyph glyph-name="uni0428" unicode="Ш" horiz-adv-x="969"
|
||||
d="M255 -2q-51 1 -101 1.5t-101 1.5q-10 0 -15 1q-17 2 -22.5 8.5t-5.5 23.5q-2 69 -1 136t0 136q-1 55 -0.5 110t0.5 111q1 23 0 37t-2 37q0 7 1 13t2 13q5 15 20 14q10 -1 26 -1.5t32.5 -0.5t30 0.5t16.5 0.5q13 1 21 0t12.5 -4.5t6.5 -11t3 -20.5q5 -62 4 -118t1 -119
|
||||
q1 -42 -1 -86t0 -86q1 -14 4.5 -17.5t18.5 -4.5q25 -2 54 -0.5t53 0.5t29 3.5t4 27.5q0 38 -1 75.5t-1 75.5q0 54 0.5 106.5t0.5 106.5q0 16 1 26.5t-1 26.5q-2 20 16 20q34 -2 65.5 0t65.5 -1q14 -2 18 -7t4 -22q1 -87 1 -172.5v-172.5q0 -19 0.5 -37.5t1.5 -36.5
|
||||
q1 -14 5.5 -18t18.5 -4q27 -1 54 -0.5t55 0.5q17 0 17 16v55q-1 41 -0.5 81t-1.5 82q-2 50 -2.5 98.5t-2.5 98.5q0 4 -0.5 7.5t-0.5 8.5q0 11 4.5 15.5t16.5 4.5h60t61 -1q11 -1 17 -6t7 -22q3 -97 4.5 -193.5t-3.5 -194.5q-2 -45 -0.5 -87.5t1.5 -87.5q0 -31 -8.5 -39
|
||||
t-38.5 -8q-17 0 -59.5 -0.5t-97 -0.5h-115.5h-113.5t-90.5 0.5t-48 0.5z" />
|
||||
<glyph glyph-name="uni0420" unicode="Р" horiz-adv-x="614"
|
||||
d="M6 483q1 36 1.5 63.5t1.5 62.5q0 10 0.5 16t3.5 9t8.5 4t15.5 2q8 1 15.5 0.5t15.5 -0.5q11 0 23 0.5t23 0.5q7 0 23 0.5t34 2t34.5 2t23.5 0.5q20 -2 39 -3.5t38 -3.5q45 -5 81.5 -25t70.5 -47q5 -4 8.5 -8t8.5 -9q24 -22 35.5 -43.5t13.5 -44.5t-3 -48t-14 -55
|
||||
q-21 -67 -60 -104t-102 -56q-35 -11 -70.5 -13t-72.5 0q-23 2 -28 -2.5t-6 -27.5q-1 -34 -0.5 -68t0.5 -68q0 -20 -5 -25t-26 -4l-55.5 2l-56.5 2q-19 1 -20 23q-1 27 -1 54v54v202v17v41t0.5 50.5t1.5 46.5zM230 495q-8 0 -17 -0.5t-17 -0.5q-9 -1 -14.5 -4.5t-6.5 -13.5
|
||||
q0 -29 -0.5 -60t-1.5 -60q0 -15 16 -17q36 -4 70 0.5t66 21.5q18 10 28 26q8 12 10 28.5t-3 31.5t-16 26.5t-29 14.5q-11 2 -25 3.5t-27.5 2.5t-23 1h-9.5z" />
|
||||
<glyph glyph-name="uni041C" unicode="М" horiz-adv-x="979"
|
||||
d="M5 622q0 12 3.5 16t16.5 5q15 1 40.5 1t51 -1t45 -1t24.5 1q18 4 25 -12q28 -58 52.5 -103l47 -84.5l44.5 -77.5l45 -81q8 -15 17 -26q7 -11 15 0l102 165l23.5 40l31.5 56l29 53.5l17 32.5q16 36 55 33q30 -2 60 -2.5t60 -1.5q10 0 19 -3q8 -2 13 -6.5t5 -14.5
|
||||
q0 -41 1 -81.5l2 -81.5q2 -66 2.5 -132t2.5 -132l2 -81l2 -82q1 -18 -7 -25t-25 -6l-105 3q-12 0 -19 1t-10 4t-3.5 9t-0.5 18q0 68 -0.5 131.5t-0.5 132.5v9.5t-1 9.5t-1.5 11t-4.5 6q-3 1 -9.5 -7.5t-8.5 -10.5l-85.5 -128.5t-78.5 -131.5q-2 -3 -3.5 -6.5t-3.5 -6.5
|
||||
q-10 -16 -15 -25t-11 -12.5t-16 -4t-30 -0.5q-30 1 -59 0q-26 -3 -34 23l-5.5 15t-5.5 14q-11 25 -22 46t-22 46l-16.5 35.5l-16.5 35.5q-6 13 -11.5 26.5t-12.5 26.5q-5 11 -13.5 26t-12.5 15q-1 0 -1.5 -14.5t-0.5 -38v-53.5t0.5 -60l1 -57.5t0.5 -47.5v-11t-1 -12
|
||||
q-3 -21 -24 -19q-32 1 -63.5 0t-63.5 1q-18 1 -22.5 5.5t-5.5 21.5q-1 29 -1 53t-1 53q-1 39 -1.5 77.5t-0.5 77.5q0 13 0.5 25.5t0.5 24.5v15v39.5v56v64.5t-0.5 64t-0.5 56z" />
|
||||
<glyph glyph-name="uni041E" unicode="О"
|
||||
d="M654 323q-2 -46 -20 -97.5t-51 -101.5q-35 -51 -83 -82t-103 -52q-16 -6 -34.5 -10t-37.5 -5.5t-37.5 -0.5t-34.5 5q-18 4 -46 15.5t-60 29t-64 41.5t-58 54q-23 27 -34.5 59.5t-13.5 68.5q-3 54 4.5 106t30.5 102q12 26 32 54.5t51 54.5t74 48t102 36q29 6 65.5 5
|
||||
t74.5 -11q28 -7 56.5 -22t53.5 -33t44 -36.5t30 -32.5q31 -40 44.5 -86t14.5 -109zM302 154q27 -1 48.5 2.5t41.5 14.5q51 32 73 79t11 106q-5 29 -20.5 53t-38.5 41t-50.5 25t-56.5 4t-55.5 -12.5t-45.5 -31.5q-37 -42 -51 -91.5t1 -106.5q3 -10 10.5 -20t15.5 -17
|
||||
q27 -22 56.5 -35t60.5 -11z" />
|
||||
<glyph glyph-name="uni0429" unicode="Щ" horiz-adv-x="1083"
|
||||
d="M340 617q0 15 3 19.5t18 4.5q30 1 60 0.5t60 -1.5q17 0 17 -20q0 -44 0.5 -84t-0.5 -84q0 -66 -0.5 -132t-0.5 -132q-1 -17 2.5 -21t20.5 -5q29 -1 58.5 -1h58.5q13 0 20 1t10.5 4.5t4 10.5t0.5 19q0 42 -0.5 83.5t-0.5 83.5q-1 61 -1 122v122q0 11 0.5 17.5t3.5 10
|
||||
t9 4.5t17 1q30 1 60 -0.5t60 -3.5q12 0 14 -15q2 -20 3.5 -40.5t1.5 -40.5q1 -80 0 -159t2 -159l1.5 -41t5.5 -17.5t17 -4t36 -0.5l36.5 -2t36.5 -3q19 -2 20 -19q0 -7 1.5 -13.5t2.5 -13.5q2 -47 2.5 -93t2.5 -93q0 -6 -1 -11.5l-2 -11.5q-3 -18 -21 -18q-27 1 -54 1t-54 1
|
||||
q-19 0 -24 5t-7 23q-5 33 -6 66q-1 11 -5.5 16t-15.5 5q-20 0 -39 0.5t-39 0.5q-105 1 -209 1.5t-210 1.5q-67 0 -133.5 0.5t-133.5 0.5h-12t-11 2q-11 2 -17 9t-3 18q-2 21 -2 39v34.5t0.5 32.5t0.5 33q-2 52 0 103t-1 103q-4 60 -3.5 113.5t1.5 113.5v16q1 17 4.5 21
|
||||
t20.5 3q17 0 34.5 -1t34.5 -1q15 -1 29.5 -1.5t28.5 0.5q19 0 19 -18q-1 -23 -1.5 -40t-0.5 -40q1 -69 1.5 -138t0.5 -138q1 -17 0.5 -33t-0.5 -33q0 -21 3 -25.5t23 -5.5l52.5 -4t52.5 -2q15 0 22.5 1t10.5 4.5t3.5 11t0.5 20.5q-1 52 -1 104t-1 105l0.5 22.5t0.5 54.5
|
||||
v67.5v62.5z" />
|
||||
<glyph glyph-name="uni0427" unicode="Ч" horiz-adv-x="656"
|
||||
d="M550 471q-1 -107 -0.5 -213t-0.5 -213q0 -11 -0.5 -16.5l-1.5 -16.5q-2 -14 -6.5 -18t-18.5 -3q-30 2 -60 0t-60 2q-26 3 -26 36v117q0 14 -1 28.5l-2 29.5q-1 17 -17 9q-5 -2 -10.5 -5.5t-10.5 -5.5l-41.5 -16t-41.5 -14q-52 -14 -96.5 0t-85.5 44q-24 18 -39.5 42.5
|
||||
t-17.5 55.5l-4 67.5t-3 68.5v43t0.5 39l1 39t0.5 43q0 22 20 22q5 0 21.5 0.5t36.5 0.5h39h30q12 -1 19 -2t10 -4t3.5 -9.5t0.5 -17.5q-1 -42 -2.5 -74.5t-2.5 -73.5q0 -17 1 -33t5 -33q5 -24 21 -38.5t45 -14.5q50 -1 90 29q14 11 21.5 23.5t7.5 32.5q-2 48 -1 93t1 93v8
|
||||
q1 17 5.5 22t21.5 5h19h33.5h36h28.5q20 1 25 -5t7 -26q1 -17 1 -41.5v-46.5t-0.5 -37.5z" />
|
||||
<glyph glyph-name="uni0426" unicode="Ц"
|
||||
d="M6 611q-1 16 2.5 20t20.5 7q4 0 19.5 0.5t35.5 0.5t40 -0.5t32 -0.5q11 0 14.5 -4.5t3.5 -14.5q1 -28 1.5 -68t0.5 -85t-0.5 -90.5t-0.5 -84.5t-0.5 -67.5t0.5 -38.5q0 -22 24 -22h73.5t73.5 -1q20 0 18 20q-1 16 -1 47.5v72t-0.5 85.5t-0.5 87t-1 77.5t-2 56.5v10t1 10
|
||||
q3 15 19 15q30 -2 60 -2.5t60 -2.5q16 -2 19.5 -5.5t4.5 -20.5q1 -24 2 -58t1.5 -73t0.5 -78.5t0.5 -73.5t0.5 -59v-34q1 -11 1 -21.5t1 -21.5q1 -17 2 -25.5t5.5 -13t13 -5.5t25.5 -1q3 0 6 0.5t5 -0.5q18 -1 27 -2t12 -5t3 -12t1 -21q4 -43 5 -83t0 -87q0 -15 -6 -23.5
|
||||
t-21 -9.5q-26 0 -49 0.5t-49 -0.5q-22 0 -25 20q-2 14 -1 28.5t-1 28.5q-2 17 -17 18q-9 1 -19 1.5t-19 -1.5q-17 0 -46 -0.5t-63.5 -0.5t-71 -1t-69 -1t-58 -0.5t-37.5 -0.5h-31q-10 1 -21 0.5t-18 4.5q-5 4 -4.5 15t-0.5 21q-3 97 1 192t0 192l-2 68q-1 29 -1 61.5v60.5z
|
||||
" />
|
||||
<glyph glyph-name="uni042C" unicode="Ь" horiz-adv-x="594"
|
||||
d="M146 -2q-30 1 -52.5 1.5t-42.5 1.5q-16 0 -31 3q-7 2 -11.5 6.5t-2.5 12.5q0 48 -0.5 98t0.5 99v61.5v77v81.5v76v60v34q0 5 -0.5 9.5t0.5 9.5q3 20 24 20q30 -2 60 -3t60 -3q14 -1 22 -2t11.5 -5t4.5 -12t1 -22q1 -34 0.5 -59.5t1.5 -59.5q0 -18 3 -22t20 -5
|
||||
q22 -2 49 -1.5t49 -1.5q84 -6 142 -64q48 -49 54 -122q5 -83 -38 -156q-22 -36 -53 -60.5t-73 -35.5q-52 -14 -103 -18t-95 1zM191 251q2 -10 2 -29q0 -9 0.5 -18.5t0.5 -19.5q1 -26 6.5 -32t31.5 -4q15 1 30 6q14 4 28 10t25.5 16t18.5 23.5t7 33.5q0 19 -8 31t-21.5 19.5
|
||||
t-30 10.5t-34.5 5q-1 1 -4 1q-19 2 -29.5 2t-15.5 -5t-6 -16.5t-1 -33.5z" />
|
||||
<glyph glyph-name="uni042A" unicode="Ъ" horiz-adv-x="666"
|
||||
d="M76 324q-1 39 0.5 77.5t-0.5 77.5q-2 18 -6 21.5t-23 4.5q-23 1 -37.5 0t-23 3t-11 16t-1.5 39q1 14 0 27t-1 27q1 14 5 18.5t18 5.5h37.5t63.5 -0.5l69 -1t54 -1.5q3 1 9.5 1t12.5 -2.5t10.5 -8.5t4.5 -16v-28v-39v-42v-38q0 -22 20 -22q37 0 73 -3q51 -5 94 -24.5
|
||||
t76 -58.5q32 -38 41 -81t-2 -91q-14 -60 -50.5 -100t-94.5 -63q-41 -16 -81.5 -19t-82.5 -5q-37 -2 -74 0l-74 4q-18 1 -21 4t-3 20q-1 48 -1.5 95t-1.5 95v12v28.5t0.5 35t0.5 32.5zM302 136q7 1 16.5 1.5t19.5 2.5q8 2 16.5 6t13.5 10q19 20 25.5 43t-2.5 50
|
||||
q-15 49 -65 58q-21 4 -42 6q-17 2 -21.5 -2t-4.5 -22q-2 -66 0 -132q0 -14 3.5 -17t19.5 -4h21z" />
|
||||
<glyph glyph-name="uni042B" unicode="Ы" horiz-adv-x="823"
|
||||
d="M7 283q1 36 1.5 89l1 104t0.5 90v48q-1 18 3 22t23 5h23t40 -0.5l43 -1t32 -0.5q16 -1 19 -3t5 -18q2 -34 0.5 -67.5t0.5 -67.5q0 -13 1 -20.5t4 -11t10 -5t21 -3.5q10 -1 19.5 -1t19.5 -2q60 -10 105 -41t72 -88q18 -38 23 -77t-7 -81q-11 -40 -36 -74.5t-66 -50.5
|
||||
q-42 -17 -81.5 -20.5l-83.5 -7.5q-5 0 -24 -0.5t-44 -0.5t-50 -0.5t-40 -1.5q-23 -1 -28.5 4.5t-5.5 28.5q0 66 -0.5 130.5t-0.5 122.5zM194 167q0 -8 3 -13.5t11 -5.5q10 0 24.5 0.5t28.5 3.5t26.5 7.5t20.5 12.5t12.5 20t6 25t-0.5 26t-7 23t-18.5 18.5t-29.5 14t-33 8
|
||||
t-29 1.5q-14 -2 -15 -14v-127zM543 615q0 14 3.5 19.5t17.5 4.5h62t62 -2q17 -1 20.5 -5.5t3.5 -22.5q2 -42 2.5 -79t0.5 -79v-194t-2 -194q0 -30 0.5 -43.5t-5 -18t-20 -3.5t-44.5 1q-19 -1 -37 0t-37 1q-18 0 -24 5t-6 24v150.5t-1 151.5l1 30.5t1.5 74.5t1 93t0.5 86z
|
||||
" />
|
||||
<glyph glyph-name="uni0411" unicode="Б" horiz-adv-x="677"
|
||||
d="M156 -12q-31 1 -61.5 1t-62.5 2q-17 1 -22 6t-5 22q1 34 1.5 67.5t1.5 68.5v135v246q0 44 -3 89q0 11 4 14.5t14 3.5h38.5h60.5t71.5 -0.5t73 -0.5h65.5t47 1q11 1 21.5 1.5t21.5 -0.5q18 -1 25 -8t6 -26q-1 -25 -3 -47.5t-4 -47.5q-1 -8 -2 -13t-3.5 -7t-7.5 -2.5
|
||||
t-14 -0.5q-35 -1 -69.5 -0.5t-69.5 -0.5q-22 0 -45 -0.5t-44 -0.5q-16 0 -18 -16q-2 -17 -0.5 -33t0.5 -34q0 -9 5 -12t13 -3q28 -3 55.5 -2.5t56.5 0.5q29 -1 57 -7.5t56 -15.5q35 -12 60.5 -35t42.5 -55q14 -26 13 -51t-7 -62q-9 -56 -38 -90.5t-66.5 -53.5t-77.5 -25
|
||||
t-73 -6h-21.5h-39h-37t-16.5 -1zM233 256q-8 2 -17 1t-19 0q-20 2 -21 -17q0 -23 -0.5 -44.5t-0.5 -44.5q0 -17 18 -20q57 -12 110 11q14 6 22.5 17t11.5 24t-0.5 25.5t-14.5 22.5q-10 8 -22.5 12.5t-25.5 7.5q-9 3 -19.5 4t-21.5 1z" />
|
||||
<glyph glyph-name="uni041A" unicode="К" horiz-adv-x="677"
|
||||
d="M7 611q0 15 4 19.5t18 3.5q24 0 45 -1t46 -2q5 -1 11 -1h12q20 0 24 -3.5t5 -23.5q2 -32 2.5 -64t1.5 -64q1 -6 0 -11.5t0 -11.5l2 -10.5t3 -10.5q5 2 9 4.5t7 5.5l89 86.5l88 87.5q11 10 22 16.5t25 5.5q38 -1 75.5 -1.5t75.5 -1.5q3 0 7 -3t5 -5q1 -3 -1 -7.5t-4 -6.5
|
||||
l-76 -77.5l-76 -77.5q-14 -14 -28.5 -26.5t-28.5 -26.5q-14 -13 -14.5 -18t9.5 -21l41.5 -67.5l42.5 -67.5l61 -96.5l61 -96.5q8 -12 3.5 -22t-18.5 -12q-5 -1 -11 -1h-12q-21 -1 -42.5 -1t-42.5 -2q-23 -2 -39.5 6.5t-29.5 26.5q-14 20 -31 47t-31 47l-42 60.5l-42 60.5
|
||||
q-8 12 -13.5 12.5t-16.5 -9.5q-20 -16 -19 -44q2 -45 2.5 -89t1.5 -89q1 -20 -3.5 -25.5t-23.5 -5.5q-30 0 -64.5 1t-64.5 3q-23 1 -24 25q-1 18 -1 63v103.5v124v124.5v105v65z" />
|
||||
<glyph glyph-name="uni041B" unicode="Л" horiz-adv-x="751"
|
||||
d="M635 356q0 -48 -0.5 -95t0.5 -95q1 -42 2 -76t3 -76q1 -17 -13 -17q-35 1 -70 1.5t-70 1.5q-10 0 -12 5t-2 13v54.5t-1 55.5l-3 213q-1 25 0 50t0 51q0 11 -1 17.5t-4.5 9.5t-10 4t-18.5 2h-8q-10 0 -15.5 -0.5t-9 -2.5t-6 -7.5t-5.5 -14.5l-23.5 -84t-25.5 -83
|
||||
q-15 -45 -32 -89.5t-39 -86.5q-12 -23 -31.5 -42.5l-39.5 -38.5q-17 -17 -39 -23.5t-46 -7.5q-33 -2 -64 5t-61 18q-14 5 -17.5 11.5t-1.5 21.5q6 29 11.5 59t12.5 59q4 21 25 15l9 -3.5l9 -3.5q31 -11 59.5 -2.5t46.5 35.5q8 12 14.5 25t10.5 27l27 91.5t24 91.5
|
||||
q11 42 19 85t17 85q3 17 7.5 21.5t21.5 6.5q4 0 8 0.5t7 -0.5q17 0 43 0.5t56.5 0.5h62.5h61h52h35q18 -1 21.5 -5t3.5 -23q0 -47 -0.5 -93t-1.5 -93v-21.5t0.5 -24t1 -20z" />
|
||||
<glyph glyph-name="uni042F" unicode="Я" horiz-adv-x="687"
|
||||
d="M571 34v-23q-2 -20 -21 -18h-127q-18 1 -24 7t-6 23v54.5t1 54.5q0 17 0.5 34.5t0.5 35.5q0 15 -3.5 18t-18.5 5q-30 6 -46 -20l-57.5 -85t-55.5 -86q-7 -12 -12.5 -19.5t-13 -12.5t-19.5 -7t-31 -2h-34t-38 1q-21 0 -44 1q-6 2 -10 5q-8 6 -1 16q14 20 30 39.5t31 40.5
|
||||
l48 67.5l48 68.5q9 14 8.5 18.5t-14.5 10.5q-35 16 -55 43.5t-34 60.5q-14 35 -18.5 70.5t6.5 73.5q5 18 16 32.5t25 26.5q49 41 113 57q45 11 87 11.5t82 1.5q17 -1 42.5 -1t49 0.5t40.5 0.5h19q21 -3 28 -11t7 -29v-48v-87t0.5 -111t0.5 -119v-111.5v-87.5zM407 438
|
||||
q-1 4 -1 12.5t-1 17.5q-2 15 -14 16q-15 1 -43.5 2.5t-56 -1t-48.5 -12t-22 -30.5q-1 -37 17 -53.5t41 -21.5t44.5 -3.5t29.5 0.5q22 -2 32.5 1t14.5 11t4.5 23t2.5 38z" />
|
||||
<glyph glyph-name="uni0414" unicode="Д" horiz-adv-x="791"
|
||||
d="M570 179q1 -20 4.5 -24.5t23.5 -5.5q13 -1 25.5 -1t24.5 -1q15 0 21 -4t9 -18q1 -6 1 -12v-11q0 -33 0.5 -66t0.5 -66v-11.5t-1 -11.5q-3 -17 -22 -17q-21 0 -42.5 -0.5t-42.5 -0.5q-20 0 -17 20v3q0 17 -1 26t-5 13.5t-13 5.5t-25 1q-12 0 -42 0.5t-69 1t-82.5 1
|
||||
t-82 0.5h-67.5t-39 -1q-4 -1 -8 -6t-5 -10q-2 -11 -1.5 -27t0.5 -27q0 -6 -2.5 -11t-10.5 -5q-26 -1 -55 -2t-55 0q-14 0 -18 5t-5 19q-2 44 -1 87t-1 87q-1 14 2.5 17.5t17.5 1.5q22 -3 36 16l27.5 35t24.5 37q22 39 39.5 79.5t27.5 84.5q2 9 8 31.5t12 49t10 51.5t4 40
|
||||
q-1 20 1.5 36.5t3.5 36.5q1 19 20 18q49 0 97 0.5t96 -0.5q72 0 143 -3q17 -1 20.5 -5t3.5 -21q0 -19 0.5 -53.5t1.5 -77l2 -88.5t1.5 -88t1.5 -76.5t2 -52.5zM400 314v18t-1 44t-1.5 55t-1.5 51q0 5 -2 11t-4 6q-11 1 -22.5 1t-22.5 -2q-9 -2 -8.5 -9t-0.5 -14
|
||||
q-2 -16 -3 -29t-3 -30q-8 -65 -27.5 -128t-51.5 -121q-1 -3 -2 -5.5t-2 -5.5q-3 -6 -0.5 -9.5t8.5 -3.5q31 0 67.5 1t67.5 2q17 0 17 17v17.5t-1 17.5q-2 29 -3 58t-3 58z" />
|
||||
<glyph glyph-name="uni0416" unicode="Ж" horiz-adv-x="928"
|
||||
d="M352 369q2 0 3 12.5t2 29.5t1 36t-1 33q-2 38 -4.5 69.5t-4.5 69.5q-1 17 2.5 21t20.5 5q32 1 64 0.5t64 -0.5q11 0 13.5 -5t2.5 -15q0 -61 1 -116t2 -116v-8t2 -7q1 -2 4.5 -3t3.5 0q5 5 9 10t7 11l57.5 108t57.5 109q7 15 16.5 23t27.5 6q25 -2 50 -1t50 1q9 0 17 -1
|
||||
t13 -12q4 -10 1 -17t-8 -15l-74.5 -114.5l-74.5 -114.5q-9 -15 -9 -29t8 -29l24 -46l30 -57l31.5 -60.5l28.5 -56l22 -44t12 -23.5q9 -19 -11 -22q-17 -3 -34 -3q-23 2 -46.5 1t-46.5 1q-19 1 -27 18l-59 114.5l-60 114.5q-2 3 -3.5 6.5t-3.5 7.5q-3 3 -6 3q-4 0 -4 -5
|
||||
q-1 -7 -1.5 -13.5t-0.5 -13.5q2 -57 3 -111l2 -112q1 -10 -5 -16.5t-15 -3.5q-32 -1 -62 0.5t-61 -0.5q-17 -2 -20.5 2.5t-4.5 21.5l-2 114.5l-2 115.5v9.5t-1 9.5q0 5 -5 5q-5 2 -7 -2l-5.5 -7.5t-5.5 -8.5l-48.5 -81l-49.5 -82l-28 -44l-28 -44q-5 -8 -9.5 -9t-22.5 0
|
||||
q-3 2 -18 2.5t-34.5 0.5h-39h-30.5q-29 2 -12 25q8 13 17 24t17 24l27.5 41.5l31.5 48.5l32 49.5l29 44.5l21 32.5t9 13.5q14 20 2 40l-21.5 39l-21.5 39q-23 38 -45 69t-45 69q-11 18 -21 36.5t-21 36.5q-10 17 11 17q13 0 33.5 -0.5t39.5 -0.5t33.5 -0.5t16.5 -0.5
|
||||
q10 2 22 1.5t18 -4.5q9 -6 14.5 -17l10.5 -21l30 -55.5l30 -52l30 -51.5l31 -55q2 -3 4 -9t7 -6z" />
|
||||
<glyph glyph-name="uni0425" unicode="Х" horiz-adv-x="771"
|
||||
d="M649 -3q-47 -2 -90.5 -1t-89.5 -1q-11 0 -17 4t-11 12q-14 18 -27.5 39t-27.5 40l-22.5 30.5l-22.5 30.5q-14 19 -19 19t-18 -19l-31.5 -47l-31.5 -48l-8 -10.5t-7 -11.5q-26 -54 -88 -48q-32 -1 -54.5 0t-48.5 1q-10 0 -22 5q-12 3 -5 15l48.5 73l48.5 73l49 75l50 74
|
||||
q8 11 7 19.5t-8 17.5l-44.5 62.5l-43.5 62.5l-49.5 74l-49.5 74q-2 5 -4 9t-4 9q-3 12 10 12q41 1 81.5 1.5t81.5 1.5q8 0 12 -3.5t8 -9.5l50 -69.5l50 -69.5q2 -3 5 -6t5 -6q11 -14 21 0q5 8 10.5 15.5l11.5 15.5l38 54t37 56q15 21 38 21q33 0 66 0.5t67 1.5q9 0 12 -5
|
||||
t2 -13q-3 -12 -10 -24l-55.5 -85l-54.5 -84l-28 -45.5l-29 -44.5q-6 -9 -3.5 -14.5t7.5 -12.5l48.5 -60.5l48.5 -61.5l54 -73.5l53 -74.5q5 -7 9 -14t8 -15q6 -12 3 -16.5t-16 -4.5z" />
|
||||
<glyph glyph-name="uni0424" unicode="Ф" horiz-adv-x="812"
|
||||
d="M457 646q4 -3 5 -14t1.5 -24t0.5 -23.5t2 -12.5q2 -5 7.5 -6l10.5 -2q88 -8 144 -38t76 -71q19 -41 30 -84q16 -66 -4 -121.5t-61 -103.5q-29 -35 -70 -53.5t-87 -24.5q-17 -3 -25.5 -5t-13 -6.5t-5 -13.5t-0.5 -25v-16q0 -18 -19 -21q-33 -3 -65.5 -3.5t-65.5 6.5
|
||||
q-20 3 -20 23q-1 14 -2.5 28.5t-2.5 28.5q-14 3 -28 5.5t-28 5.5q-20 5 -39.5 9t-39.5 10q-26 9 -50.5 23t-45 35t-34.5 49t-20 64q-10 57 4.5 107.5t45.5 97.5q17 25 40 42t51 28q29 12 60.5 19t63.5 8q10 0 16 1t8.5 4t3 9.5t0.5 17.5v15q-3 27 5.5 33.5t33.5 8.5
|
||||
q6 1 23.5 0.5t37 -1.5t36 -3.5t20.5 -5.5zM298 337h1q0 17 0.5 33t0.5 33q-1 17 -4 22t-21 3q-9 0 -26.5 -8.5t-34.5 -23t-29.5 -33.5t-11.5 -41q1 -32 13.5 -53.5t28 -34.5t32 -18t27.5 -5q9 1 15.5 3t7.5 16q0 27 0.5 53.5t0.5 53.5zM462 225q0 -17 15 -14q24 5 45.5 14.5
|
||||
t35.5 29.5q20 26 21.5 56t-9.5 61q-11 26 -32 35t-44 14q-2 0 -8 2q-14 2 -18 -0.5t-5 -16.5q0 -11 -0.5 -36t-0.5 -53.5v-54v-37.5z" />
|
||||
<glyph glyph-name="uni0423" unicode="У"
|
||||
d="M159 641q14 0 22 -1.5t11.5 -5t3.5 -11t-2 -19.5q-5 -29 -0.5 -56t12.5 -54q16 -53 53 -81t84 -45q13 -5 17.5 -2t11.5 18l24 59l23 60l21 56l21 56q5 18 21 18l70 2t70 0q23 0 30 -9t-1 -32q-21 -62 -45 -120q-35 -94 -79 -181.5t-94 -173.5q-16 -28 -38 -49t-49 -38
|
||||
q-28 -17 -57.5 -30.5t-64.5 -16.5q-20 -1 -40 -0.5t-40 7.5q-28 9 -55.5 17t-52.5 25q-15 10 -16.5 14.5t5.5 22.5q10 28 18.5 55.5t19.5 54.5q8 22 18.5 24.5t30.5 -11.5q11 -8 22.5 -14.5t24.5 -11.5q38 -14 74 2t57 47q11 14 -7 20l-39 16l-39 16q-38 17 -68.5 42.5
|
||||
t-56.5 54.5q-27 31 -40.5 68t-20.5 77q-5 29 -8 54.5t-6 55.5q-1 14 2 17.5t17 3.5h28.5h40.5t39.5 -0.5t26.5 -0.5z" />
|
||||
<glyph glyph-name="uni0401" unicode="Ё" horiz-adv-x="593"
|
||||
d="M475 634q19 0 21 -18q0 -4 0.5 -20t0.5 -34t-0.5 -33t-0.5 -16q-1 -14 -4.5 -18t-18.5 -4q-16 0 -52.5 -0.5t-78 -1t-78.5 -1.5t-54 -1q-15 0 -20.5 -5.5t-6.5 -19.5q-2 -20 -2 -32v-31q0 -20 23 -20q21 0 41.5 -1l41.5 -2q19 -2 15 -21q-2 -14 -1.5 -28.5t-1.5 -28.5
|
||||
q-2 -17 -7.5 -21.5t-22.5 -4.5t-33 0.5t-33 0.5q-14 -1 -19 -5t-5 -18q1 -20 0 -40.5t1 -40.5q2 -17 7.5 -20.5t21.5 -2.5q27 0 56 0.5t56 0.5q34 -1 68 -1.5t68 -2.5q18 -1 24 -7t5 -23v-24.5t0.5 -28.5t0.5 -24.5v-12.5q-1 -16 -6.5 -21t-22.5 -5h-40h-75.5h-93.5h-93.5
|
||||
h-76h-40.5q-13 0 -20 0.5t-10.5 4t-4.5 10.5t-1 19q-1 61 -1 120t-1 120v89q1 43 2 78.5t2 78.5q1 24 0 48t0 48q0 17 3.5 20.5t20.5 3.5q47 1 93 0.5t93 -0.5l28.5 0.5t69 0t85 -1.5t77.5 -2zM127 695q-16 1 -29.5 7t-23.5 16.5t-15.5 23.5t-4.5 28q1 13 7.5 25t16.5 21
|
||||
t22.5 14.5t25.5 5.5q12 0 24.5 -6.5t22 -17t15.5 -23t6 -24.5q0 -15 -5.5 -28t-14.5 -22.5t-21.5 -15t-25.5 -4.5zM396 831q12 0 23.5 -5t20.5 -13.5t14 -19t5 -22.5q-1 -30 -18.5 -49.5t-44.5 -18.5q-29 0 -48 19.5t-19 49.5q-1 26 18 42.5t49 16.5z" />
|
||||
<glyph glyph-name="uni0419" unicode="Й" horiz-adv-x="708"
|
||||
d="M6 621q0 16 6 23t21 5q35 -2 67.5 0t67.5 -2q22 -2 20 -23v-35.5v-61.5t0.5 -73.5t1 -71t1.5 -53.5t2 -21q11 0 15 6q14 17 26.5 34l25.5 34l45.5 65l45.5 65l39.5 55l39.5 56q5 7 8.5 11t9 6.5t13 3t21.5 0.5h78q16 0 24 -0.5t12 -3.5t4.5 -10.5t1.5 -20.5
|
||||
q1 -25 1.5 -73t0.5 -106.5v-121t-0.5 -115t-0.5 -89.5v-45q0 -12 0.5 -20.5t-1.5 -20.5q-2 -13 -7 -17t-19 -3q-26 2 -52.5 0.5t-52.5 0.5q-9 1 -21.5 0t-15.5 2t-2 19t0 27q0 44 0.5 79t-0.5 79q0 23 -0.5 46.5t-1.5 46.5q0 7 -4 10q-4 2 -9 -3q-4 -4 -10 -12l-79 -103.5
|
||||
l-79 -103.5q-14 -20 -28.5 -37t-28.5 -37q-10 -15 -29 -15q-35 2 -69.5 1t-69.5 1q-8 0 -10.5 4t-3.5 11v13.5t-1 13.5q0 70 -0.5 139.5t-1.5 140.5q-1 0 -1 21v52.5v69v71v57.5v29zM308 710q-15 0 -29 6t-24 15.5t-16 21.5t-5 25q2 29 25 48.5t55 18.5q65 -1 68 -72
|
||||
q1 -12 -5.5 -23.5t-17 -20.5t-24 -14t-27.5 -5z" />
|
||||
<glyph glyph-name="uni042E" unicode="Ю" horiz-adv-x="969"
|
||||
d="M184 23q-1 -16 -5 -20.5t-19 -4.5q-29 1 -59 1t-59 2q-13 0 -20 1t-10 4.5t-3.5 10t-0.5 18.5q-2 79 -0.5 157t-0.5 157q-1 32 -1.5 64.5t1.5 64.5q2 37 2 70.5t-2 71.5q-1 14 1.5 18t13.5 4q36 0 69 -0.5t69 -0.5q16 -2 16 -17q1 -14 1 -27v-27q1 -37 0 -70.5t1 -71.5
|
||||
q0 -19 3.5 -22t21.5 -5q14 -1 27 -0.5t26 -0.5q17 -1 23 16q5 11 9.5 22.5t9.5 22.5q34 78 95 121t142 61q66 15 123 -1.5t107 -58.5q35 -31 60.5 -69t37.5 -86q9 -36 15 -72t2 -74q-5 -44 -17 -83.5t-38 -76.5q-19 -26 -40 -50t-49 -41q-53 -32 -109.5 -41.5t-117.5 -6.5
|
||||
q-42 3 -79 18t-72 38q-33 22 -52.5 54t-30.5 69q-7 22 -11.5 44l-9.5 45q-4 17 -20 18q-18 1 -28 0t-15.5 -6.5t-7 -15.5t-1.5 -28q0 -11 0.5 -38t0.5 -57.5t0.5 -59t0.5 -41.5zM442 324q0 -6 -0.5 -12t-0.5 -11q0 -34 15 -59.5t36.5 -43t44.5 -26t38 -8.5q72 0 111 47.5
|
||||
t39 125.5q0 12 -2 33.5t-13.5 44t-34.5 41t-65 26.5q-61 10 -104.5 -22t-59.5 -90q-2 -6 -2.5 -13.5l-1 -15t-0.5 -12.5v-5z" />
|
||||
<glyph glyph-name="uni0412" unicode="В" horiz-adv-x="646"
|
||||
d="M429 377q14 -5 25 -10l22 -10q9 -4 20 -16t20.5 -32t16 -48.5t6.5 -64.5q0 -29 -14 -58t-35.5 -54.5t-47.5 -45t-50 -28.5q-36 -14 -73.5 -19t-75.5 -6q-2 0 -4 0.5t-4 -0.5q-17 -2 -49.5 -1.5t-65.5 1.5q-39 1 -82 2q-7 0 -12 2t-9 5t-6 9q-5 17 -5 33q-1 79 -1 157.5
|
||||
v157.5q0 44 0.5 87t0.5 87q1 29 0.5 57.5t-2.5 57.5q0 15 12 15q64 1 120.5 2t120.5 0q23 0 45.5 -5t45.5 -11q29 -8 53 -25.5t44 -40.5q12 -15 17 -37q4 -20 6 -39.5t0.5 -37.5t-7.5 -35.5t-19 -33.5q-2 -3 -5 -6.5t-8 -8.5zM186 205v-26q-2 -20 -2 -30t4.5 -14t14.5 -4
|
||||
t28 -1q30 -2 57 8t51 27q40 27 14 70q-10 15 -28 27q-29 19 -59.5 19.5t-62.5 -3.5q-10 -2 -13 -7t-3 -14q0 -7 -0.5 -15.5t-0.5 -16.5v-20zM183 478v-41.5t6 -19.5t20 -4t43 4q12 2 23 6.5t22 10.5q14 8 20 21.5t5 29.5t-11 25.5t-24 13.5q-12 3 -24.5 4l-25.5 2
|
||||
q-12 1 -24 0t-23 -1q-2 -13 -3.5 -25.5t-3.5 -25.5z" />
|
||||
<glyph glyph-name="uni0421" unicode="С" horiz-adv-x="688"
|
||||
d="M-20 334q0 53 15.5 96.5t40.5 77t55.5 59t61 42.5t57 27t42.5 13q94 20 179 -19q19 -8 37.5 -19.5l35.5 -22.5q11 -6 10.5 -10.5t-9.5 -13.5l-45.5 -41.5l-45.5 -41.5q-14 -13 -32 -6q-30 11 -58 9q-18 -1 -44.5 -11t-51 -30t-42 -51.5t-17.5 -75.5q0 -53 23 -84.5
|
||||
t54 -47.5t62.5 -19.5t49.5 -0.5q32 5 57 22.5t45 43.5l9 10.5t6 4.5t7 -1.5t11 -7.5l39 -25t39 -26q14 -10 16 -16.5t-5 -21.5q-15 -34 -39 -60t-55 -46q-36 -23 -74.5 -39.5t-83.5 -18.5q-92 0 -153.5 26t-100.5 65t-58.5 84.5t-28 84.5t-9 65z" />
|
||||
<glyph glyph-name="uni042D" unicode="Э" horiz-adv-x="719"
|
||||
d="M624 300q1 -40 -9 -80.5t-31 -78.5t-54 -62q-34 -25 -68.5 -44.5t-75.5 -32.5q-73 -23 -141.5 -11.5t-135.5 40.5q-52 22 -95 62q-5 5 -8.5 9.5t-0.5 12.5l27 65.5l27 64.5q5 13 17 19q18 9 34 -7q14 -14 29 -27t32 -24q49 -33 100 -32.5t103 23.5q41 18 55 64
|
||||
q6 20 -14 20h-33t-58 -0.5t-58 -0.5h-34h-17t-17 1q-20 0 -20 21q0 11 -2 21t-2 21q-1 21 6.5 28.5t28.5 8.5q6 0 21.5 0.5t35.5 0.5h42.5h42.5t35.5 0.5t21.5 0.5q14 2 16 5.5t-2 16.5q-10 29 -32.5 44t-49.5 23q-92 26 -173 -18q-17 -9 -24.5 -5.5t-15.5 16.5l-24.5 39
|
||||
l-23.5 39q-3 5 -6 9.5t-7 8.5q-10 12 -9.5 16t11.5 14q78 66 178 69q61 3 121 -6q56 -8 99.5 -37.5t78.5 -71.5q44 -52 64 -112.5t15 -132.5z" />
|
||||
<glyph glyph-name="uni0417" unicode="З" horiz-adv-x="593"
|
||||
d="M403 626q44 -21 65.5 -57.5t23.5 -75.5t-13.5 -74.5t-45.5 -54.5q-6 -4 -9 -7q-4 -4 -4 -6.5t6 -6.5q11 -7 21 -15l20 -16q18 -16 28 -41.5t12 -53.5t-4 -54.5t-19 -44.5q-39 -54 -90 -83.5t-115 -38.5q-16 -2 -31.5 -3t-30.5 -1q-29 -2 -54.5 4t-49.5 16.5t-47.5 23.5
|
||||
l-46.5 27q-8 5 -11.5 13.5t0.5 18.5l22.5 52l22.5 52q2 4 8 6t11 1t9.5 -4.5t9.5 -5.5q35 -20 72.5 -34t79.5 -17q21 -2 38 6l34 16q5 3 8.5 10t5 15.5t1.5 17.5t-3 15q-17 42 -60 48q-14 2 -27.5 2t-27.5 1q-16 0 -16 15q-2 18 -3.5 36.5t-3.5 36.5q18 5 35.5 8.5t35.5 8.5
|
||||
l24 8t23 12q14 10 20.5 23t5 26t-10 24.5t-24.5 19.5q-20 11 -41 13t-44 -6t-45 -16.5t-43 -20.5q-19 -11 -27 -9.5t-20 21.5q-13 20 -25 40.5t-23 41.5q-2 4 -1 10t4 10q5 5 11 9t13 7q44 24 87 32t91 20q6 2 24.5 3t43 -0.5t51 -6.5t49.5 -17z" />
|
||||
<glyph glyph-name="one" unicode="1" horiz-adv-x="478"
|
||||
d="M387 29q1 -6 0.5 -14.5t-2.5 -10.5q-5 -4 -9.5 -3.5t-10.5 0.5q-29 -1 -58 -1h-58q-14 0 -18.5 5t-5.5 21q-3 78 -3 157q-1 50 0 99t1 99v12t-1 11q-1 6 -4.5 8t-8.5 -3l-10.5 -8t-10.5 -9l-34.5 -29.5t-35.5 -29.5q-14 -11 -22.5 -10.5t-20.5 13.5q-17 19 -33 39l-33 40
|
||||
q-13 16 2 31l21 19t20 20q36 38 76 70l82 65q10 8 20.5 13t23.5 4q30 0 58.5 1t58.5 -1q14 -1 17 -4t4 -19q0 -15 -0.5 -31t-0.5 -31q0 -35 0.5 -69t0.5 -68q-2 -105 -3 -189q0 -36 -0.5 -70.5t-1 -62t-0.5 -45v-19.5z" />
|
||||
<glyph glyph-name="two" unicode="2" horiz-adv-x="541"
|
||||
d="M225 0h-183q-14 0 -21.5 1t-11.5 5t-4.5 12t-0.5 22v39t-1 38q-2 28 19 44l40 32l40 31q40 30 74 65t62 77q11 16 14.5 33.5t-3.5 36.5q-11 37 -51 40q-23 2 -42.5 -5t-36.5 -21q-17 -16 -33 0l-36 35.5l-36 35.5q-7 6 -5.5 12t6.5 12q33 39 75 61q47 26 96 35.5
|
||||
t103 -2.5q28 -6 51.5 -20.5t40.5 -33.5t28 -41t15 -44q16 -87 -13.5 -155t-91.5 -125l-30.5 -27.5l-30.5 -27.5q-2 -2 -2.5 -6t-0.5 -7q0 -1 3.5 -2.5t5.5 -1.5h144q5 0 9.5 0.5t9.5 0.5q14 2 16 -12q2 -28 0.5 -55t-0.5 -54q0 -17 -6 -22t-22 -7h-43q-20 0 -45 0.5l-48 1
|
||||
t-38.5 0.5t-15.5 -1z" />
|
||||
<glyph glyph-name="zero" unicode="0" horiz-adv-x="552"
|
||||
d="M462 311q-3 -32 -4.5 -59.5t-5.5 -53.5q-8 -65 -36 -115t-82 -86q-30 -20 -68 -21.5t-72 5.5q-37 7 -68.5 25t-58.5 44q-20 19 -30.5 43t-15.5 50q-15 74 -17.5 147.5t12.5 148.5q7 34 17 65t34 57l26 28t27 25q42 32 92.5 35t98.5 -15q65 -23 97.5 -71t41.5 -114
|
||||
q5 -36 7.5 -71.5zM318 298v34q0 14 0.5 29.5t0.5 26.5q0 23 -8 44.5t-21 40.5q-6 9 -15.5 15t-21.5 8.5t-24.5 0.5t-23.5 -11q-22 -17 -31 -38.5t-14 -45.5q-11 -51 -8 -101t11 -101q2 -13 6.5 -25.5t12.5 -23.5q9 -14 23.5 -21t30.5 -6t30.5 8.5t22.5 22.5q9 15 16 31t7 33
|
||||
q0 20 3 39.5t3 39.5z" />
|
||||
<glyph glyph-name="three" unicode="3" horiz-adv-x="594"
|
||||
d="M226 -12q-22 0 -47.5 9t-48.5 18q-35 13 -66 34l-62 43q-5 4 -8.5 9t1.5 13l60 90q7 11 18 3l25 -18l24 -18q28 -15 55.5 -21.5t56.5 -7.5q22 -2 43.5 8.5t32.5 30.5q5 10 5 22.5t-4 24t-12 21t-18 14.5q-12 6 -25.5 7.5t-30.5 0.5q-8 3 -13 7t-6 14q-2 13 -1.5 26
|
||||
t-1.5 27q-3 19 18 29l18.5 5.5l16.5 5t16 6t19 10.5q5 3 9.5 12t7.5 20t4 21.5t0 16.5q-2 6 -8 14.5t-17.5 14.5t-28 6t-38.5 -11q-13 -8 -26 -20.5t-24 -17.5q-13 -6 -20 -5.5t-18 10.5q-8 8 -15 17t-15 18q-12 14 -18.5 22.5t-6 15.5t7.5 14.5t22 20.5q42 35 88 58t103 23
|
||||
q63 -1 105 -30.5t62 -87.5q15 -46 5.5 -89t-44.5 -79q-7 -8 -7 -13t10 -10q28 -15 43 -39.5t19 -52.5t-1 -57t-17 -52q-9 -17 -21.5 -43t-38.5 -50.5t-70.5 -42t-116.5 -17.5z" />
|
||||
<glyph glyph-name="four" unicode="4" horiz-adv-x="593"
|
||||
d="M472 329q0 -69 -1 -130.5t-1 -121.5q0 -16 1 -30.5t1 -30.5q1 -11 -4.5 -15t-19.5 -3q-27 1 -54.5 0t-54.5 -1q-20 1 -25 5t-5 24q-1 26 -1 53t-1 53q-1 17 -5.5 22t-21.5 7q-58 3 -116 2.5t-114 -0.5q-23 -3 -32.5 4.5t-11.5 29.5q-2 20 -2 39t-1 39q-1 19 13 35
|
||||
l145 156.5l144 157.5q16 18 37 15q28 -2 55 -1t55 -1q21 0 21 -21q0 -20 1 -39t0 -39q-1 -54 -0.5 -108t-1.5 -101zM231 295h58q18 0 18 16q1 52 0 105q0 12 -6 14.5t-14 -5.5l-39.5 -38l-38.5 -39q-11 -11 -23.5 -22t-16.5 -28q7 -1 18 -1.5t20.5 -1t16.5 -0.5h7z" />
|
||||
<glyph glyph-name="five" unicode="5" horiz-adv-x="583"
|
||||
d="M233 640q39 1 77.5 1h77.5q17 0 20 -3t6 -19l8 -40.5l8 -40.5q1 -5 0.5 -9t-0.5 -9q-1 -15 -6 -19t-20 -4q-32 2 -65 2.5t-66 -0.5q-18 0 -23.5 -5.5t-8.5 -22.5q-2 -11 -6 -23.5t1 -20.5q3 -5 8 -5.5t10.5 1l11.5 3t12 1.5q42 -2 79.5 -14t70.5 -38q19 -14 30 -34
|
||||
t19 -42q10 -26 10 -62.5t-8.5 -73t-25.5 -69.5t-41 -53q-21 -17 -44.5 -27.5t-49.5 -18.5q-66 -18 -130.5 -15t-129.5 23q-18 6 -35 17l-34 22q-21 13 -13 36l20 52.5t21 52.5q7 16 24 2q26 -23 56.5 -34.5t63.5 -16.5q29 -5 58 -3t56 17q10 6 19.5 14t16.5 18q9 13 11 28.5
|
||||
t-2 30.5t-13.5 27.5t-24.5 20.5q-11 6 -24 10.5t-26 4.5q-27 0 -54 -1.5t-53 -15.5q-9 -7 -20 0q-24 16 -37.5 28t-18 25t-1.5 30.5t11 46.5l29 101.5l28 101.5q3 12 8 18t19 5q11 -1 26.5 -1h29.5h34z" />
|
||||
<glyph glyph-name="six" unicode="6" horiz-adv-x="605"
|
||||
d="M8 287q0 75 18 129.5t45 93t60 63t62 38.5t51 19.5t28 7.5q16 4 44 4t55.5 -2t48.5 -5.5t24 -7.5t6 -19.5t5 -34l4 -36.5l3 -27q1 -11 -2 -14t-14 -1q-8 1 -24.5 3.5t-37.5 4t-44 0.5t-42 -8q-12 -4 -28 -15t-31.5 -26.5t-29 -35t-19.5 -41.5q-1 -3 -1 -8t2 -6
|
||||
q2 -2 7 -0.5t7 3.5q21 17 44.5 24.5t49.5 11.5q34 6 65.5 -1t62.5 -22q18 -9 32.5 -27t25.5 -42t17 -53t6 -59q0 -61 -26.5 -104t-91.5 -82q-47 -24 -92.5 -27.5t-87.5 8.5t-78.5 36.5t-63.5 56.5q-18 21 -29.5 48.5t-18 55.5t-9.5 53.5t-3 43.5zM285 148q7 0 10.5 -0.5
|
||||
t5.5 -0.5q26 0 42.5 16.5t16.5 43.5q-1 52 -58 52q-33 -2 -53 -23l-11.5 -13t-10.5 -13q-13 -17 -8.5 -31t24.5 -22q11 -4 22.5 -5.5t19.5 -3.5z" />
|
||||
<glyph glyph-name="seven" unicode="7" horiz-adv-x="573"
|
||||
d="M225 637q39 0 78 -0.5t78 -1.5q17 -1 35.5 0.5t35.5 -0.5q12 -1 18 -7.5t7 -17.5q2 -16 0.5 -34t-0.5 -34q1 -4 -1.5 -8.5t-4.5 -8.5q-35 -54 -61.5 -111l-52.5 -115l-64 -137.5l-64 -137.5q-5 -10 -12 -17t-20 -7h-140q-17 0 -10 15l12.5 24l12.5 24l91.5 191.5
|
||||
l91.5 191.5q12 25 9 30t-32 5h-100.5t-100.5 -1q-11 0 -17 3.5t-7 14.5q-2 29 -1.5 58.5t-0.5 58.5q-1 20 21 22h20q22 0 53 -0.5t59 -0.5t47.5 0.5z" />
|
||||
<glyph glyph-name="eight" unicode="8" horiz-adv-x="541"
|
||||
d="M245 653q41 0 74.5 -13.5t60.5 -45.5q16 -20 23.5 -45t7.5 -51.5t-6 -50.5t-18 -42q-9 -13 -8 -16.5t12 -14.5q43 -39 61.5 -87.5t16.5 -105.5q-2 -39 -22.5 -74.5t-51 -63.5t-68 -44.5t-73.5 -17.5q-61 2 -117 28t-92 68.5t-44.5 98t27.5 115.5q26 47 70 81
|
||||
q9 7 11.5 13.5t-4.5 17.5q-9 14 -12 30.5l-6 33.5q-6 35 3 65t27 58q21 31 54.5 47t73.5 16zM309 186q0 11 -4.5 24t-11.5 25t-16 22t-19 15q-7 5 -17 1q-20 -10 -36 -23.5t-24 -34.5q-6 -13 -3 -28q2 -13 11.5 -24.5t22 -20t27 -11.5t27.5 0q8 2 16 8t14 13.5t9.5 16
|
||||
t3.5 17.5zM248 522q-14 -1 -22 -6t-9 -15q-2 -23 4 -42.5t25 -33.5q2 -2 6 -2t6 2q12 14 19.5 30.5t5.5 36.5q-3 32 -35 30z" />
|
||||
<glyph glyph-name="nine" unicode="9" horiz-adv-x="604"
|
||||
d="M505 337q-2 -44 -7.5 -86t-20.5 -83q-38 -101 -132 -147q-145 -71 -286 15l-23 14t-23 15q-15 12 -4 31q14 23 27 47t27 47q5 8 9.5 10t14.5 -3q40 -24 83 -43q29 -14 58.5 -11t58.5 14q33 13 48 39t18 62q1 7 -3 9t-9 -2q-5 -2 -8.5 -5t-8.5 -5q-103 -60 -198 17
|
||||
q-20 16 -36.5 33t-28.5 39q-13 22 -18.5 49.5t-3 55t12 53.5t25.5 46q22 27 52 52.5t62 36.5q12 6 35.5 8t50.5 0.5t53.5 -7t46.5 -15.5q52 -25 77 -68.5t37 -94.5q8 -30 9.5 -61t4.5 -62zM322 431q1 18 -5.5 33t-17 24.5t-25 13t-30.5 -0.5q-14 -4 -25 -12.5t-13 -22.5
|
||||
q-3 -16 -6.5 -32.5t9.5 -33.5q11 -14 32.5 -21.5t40.5 -2.5q37 9 40 55z" />
|
||||
<glyph glyph-name="hyphen" unicode="-" horiz-adv-x="302"
|
||||
d="M125 293h95q11 0 9 -11l-3 -60q-1 -15 -5 -20t-19 -6q-20 -2 -39 -2h-39h-84q-17 0 -22.5 5t-10.5 20q-1 2 -1 3t-1 3q-2 26 -2.5 40t4.5 20t17.5 6h36.5h19t21 0.5t17 1z" />
|
||||
<glyph glyph-name="equal" unicode="=" horiz-adv-x="396"
|
||||
d="M29 308q-18 0 -20 20l-2 31.5t-1 31.5q0 11 4 14.5t15 4.5h17.5h39h52h55.5h50.5h36.5q19 0 20 -18q2 -12 4.5 -24.5t4.5 -24.5q3 -26 -22 -30q-4 -1 -21 -1.5t-41.5 -1t-53 -1l-55.5 -1t-49.5 -0.5h-33.5zM154 243h120q13 0 15 -2t1 -15q0 -17 0.5 -32.5t0.5 -31.5
|
||||
q-1 -14 -5 -18t-18 -5h-11l-219 3q-19 0 -23.5 4t-6.5 23q-2 13 -1.5 26t0.5 27q-1 13 2.5 16.5t17.5 4.5h127z" />
|
||||
<glyph glyph-name="parenleft" unicode="(" horiz-adv-x="323"
|
||||
d="M246 -14q-5 -2 -9 -4t-9 -2q-22 0 -43.5 -0.5t-44.5 0.5q-13 2 -22 14l-23.5 33.5t-20.5 34.5l-28.5 63l-26.5 63q-11 25 -13 50t0 51q5 73 33.5 135t74.5 117q6 7 12 13t11 14q26 32 60.5 38t71.5 2q6 0 6.5 -4.5t-2.5 -8.5l-15 -20.5t-15 -18.5q-21 -23 -37 -49.5
|
||||
t-29 -55.5q-26 -57 -31 -117t4 -121q6 -46 22 -87.5t37 -81.5q8 -15 17.5 -29z" />
|
||||
<glyph glyph-name="parenright" unicode=")" horiz-adv-x="312"
|
||||
d="M70 -20q-18 1 -27 2.5t-11.5 6t0.5 13.5t11 25l25 52l26 52q29 60 35.5 122t-4.5 127q-11 62 -36 116t-69 100q-6 7 -13 21q-5 11 -2 15t14 5h42t42 -1q5 -1 10.5 -4t9.5 -7q38 -36 65.5 -78.5t40.5 -93.5q14 -56 21.5 -112.5t-0.5 -115.5q-5 -41 -19 -79t-34 -73
|
||||
q-11 -20 -24.5 -38.5t-24.5 -38.5q-11 -18 -32 -18h-35q-2 0 -4 1q-2 0 -3.5 0.5t-3.5 0.5z" />
|
||||
<glyph glyph-name="period" unicode="." horiz-adv-x="229"
|
||||
d="M84 -4q-4 1 -10.5 2t-14.5 2q-15 3 -26.5 12t-18.5 21.5t-9 28.5t3 32q8 26 34 38.5t55 3.5q18 -6 32 -15q42 -28 28 -73q-7 -23 -26.5 -37.5t-46.5 -14.5z" />
|
||||
<glyph glyph-name="colon" unicode=":" horiz-adv-x="209"
|
||||
d="M154 355q-5 -38 -38 -58q-13 -8 -30 -8.5t-32.5 5t-28.5 16t-18 24.5q-6 16 -2.5 31.5t13.5 29.5q13 19 36 28.5t44 2.5q24 -7 40 -27t16 -44zM75 0q-36 2 -54 20.5t-16 52.5q1 14 6.5 26.5t15 21.5t21.5 14t25 4q73 -5 70 -74q-1 -29 -20.5 -48t-47.5 -17z" />
|
||||
<glyph glyph-name="comma" unicode="," horiz-adv-x="219"
|
||||
d="M145 46q0 -20 -7.5 -40t-20 -37.5t-29.5 -30.5t-35 -18q-3 -1 -6.5 0t-6.5 1q1 3 1 6.5t1 6.5q2 6 5.5 12.5t5.5 12.5q4 14 2 18.5t-15 12.5q-24 15 -32 40t3 48q12 23 37 31.5t51 -1.5q14 -5 23 -10.5t14 -12t7 -16t2 -23.5z" />
|
||||
<glyph glyph-name="semicolon" unicode=";" horiz-adv-x="239"
|
||||
d="M159 49q0 -16 -4.5 -29.5t-12.5 -27.5q-15 -29 -39 -45t-53 -27q-5 -2 -9.5 0.5t-9.5 3.5q2 4 3.5 7.5t3.5 7.5q3 5 7 9.5t7 10.5q7 12 4.5 18.5t-13.5 15.5q-6 5 -11.5 9t-10.5 9q-12 13 -15 29.5t1.5 32.5t15 29t25.5 18q20 7 38.5 6.5t37.5 -5.5q19 -4 24 -24
|
||||
q4 -11 6.5 -23.5t4.5 -24.5zM91 282q-17 0 -32 6t-27 16t-19.5 23t-7.5 27q0 15 6.5 29.5t17.5 26t25.5 18t29.5 6.5q36 0 60 -22.5t24 -54.5q-1 -33 -23 -54.5t-54 -20.5z" />
|
||||
<glyph glyph-name="quotesingle" unicode="'" horiz-adv-x="833"
|
||||
/>
|
||||
<glyph glyph-name="slash" unicode="/" horiz-adv-x="323"
|
||||
d="M57 -3q-9 0 -17.5 -0.5t-17.5 -0.5q-14 1 -17 4.5t1 17.5q11 42 19.5 84l17 84.5l17.5 85t21 83.5q14 49 25 97.5l23 97.5q2 8 3.5 12.5t4.5 7t7.5 3t12.5 0.5q39 1 78 0q14 0 18 -5t1 -19q-5 -26 -9.5 -52t-11.5 -52l-47 -195l-48 -195q-2 -10 -5 -19l-6 -18
|
||||
q-8 -24 -31 -23q-10 1 -19.5 1h-19.5v1z" />
|
||||
<glyph glyph-name="backslash" unicode="\" horiz-adv-x="344"
|
||||
d="M195 -6q-1 0 -4 -1t-6 -1q-15 -2 -24.5 -1t-15.5 5t-10 13t-8 24l-29.5 116.5l-29.5 116.5q-6 26 -13.5 52.5t-13.5 53.5q-4 19 -6 38t-6 38l-10 39.5l-10 39.5q-2 5 -3 10l-2 10q-2 8 1 13t11 5q26 1 51 1.5t51 0.5q6 0 8.5 -4.5t4.5 -9.5l63 -257.5l62 -257.5
|
||||
q2 -5 2.5 -10t1.5 -10q4 -16 0 -21t-19 -5q-11 -1 -21.5 0t-24.5 2z" />
|
||||
<glyph glyph-name="plus" unicode="+" horiz-adv-x="407"
|
||||
d="M175 425q23 -1 35.5 -1.5t18 -6t6 -17.5t-0.5 -36q-1 -2 -0.5 -3.5t0.5 -3.5q0 -17 1 -25t5.5 -11t13 -2.5t25.5 0.5h42q17 0 17 -17q0 -37 3 -71q1 -18 -15 -18q-17 -1 -32.5 -1t-31.5 -1q-14 0 -17 -3t-4 -17q-1 -12 -1 -27.5v-27.5q0 -24 -18 -19q-17 4 -33.5 3
|
||||
l-33.5 -2t-22 5t-6 24q-1 11 -1 26v26q-1 19 -20 19q-8 -1 -16 -1h-16q-7 0 -13 -1t-12 0q-14 1 -22 2t-13 4.5t-6.5 11.5t-1.5 25q0 10 -1.5 19t-1.5 19q1 12 5 16.5t17 5.5q15 1 30 0t30 -1t23.5 1t12 4.5t4 12t0.5 23.5q-1 14 -1.5 25.5t-1.5 25.5q0 17 18 17
|
||||
q9 0 18 -0.5t17 -1.5z" />
|
||||
<glyph glyph-name="uni0410" unicode="А" horiz-adv-x="760"
|
||||
d="M259 135q-11 0 -16.5 -1t-9 -3.5t-6 -8.5t-5.5 -16q-5 -18 -11 -35.5t-11 -35.5q-7 -34 -40 -34q-36 -2 -71.5 -2t-71.5 -1q-16 0 -11 16l47.5 144.5l46.5 145.5q14 44 26 87.5t26 87.5l20.5 60t19.5 61q6 20 18 28t30 8q21 -1 41.5 -1.5t40.5 -0.5q23 0 46 0.5t46 0.5
|
||||
q13 0 25.5 0.5t25.5 0.5q12 0 19 -1t11 -4t6.5 -9.5t5.5 -18.5l40 -155.5l41 -155.5l32.5 -116l33.5 -115q6 -24 8.5 -36.5t-1 -18.5t-14 -6.5t-30.5 0.5q-34 0 -67.5 1l-67.5 2q-18 1 -21 18q-2 15 -5 30.5l-6 30.5q-2 8 -4 15t-4 15q-5 20 -24 20q-13 -1 -25.5 -0.5
|
||||
t-24.5 0.5l-12 0.5t-29 1t-36 1t-32 0.5zM360 481q-2 3 -6 -6.5t-6 -12.5l-27 -81l-23 -65l-23 -66q-5 -14 -1.5 -18t18.5 -4q24 -1 48 -1h48q19 1 22 4.5t-2 23.5q-8 41 -17 81l-18 81q-2 7 -3.5 18t-3.5 21l-3.5 17.5t-2.5 7.5z" />
|
||||
<glyph glyph-name="uni041D" unicode="Н" horiz-adv-x="667"
|
||||
d="M554 94q1 -26 1.5 -41.5t0.5 -41.5q0 -11 -4.5 -14.5t-17.5 -2.5q-29 1 -58 1.5t-58 0.5q-17 0 -20.5 3t-4.5 20q-2 47 -4.5 96t-4.5 96v11t-1 12q0 12 -15 14q-40 2 -79.5 0.5t-78.5 0.5q-20 1 -18 -19q1 -49 -1 -95t0 -99q1 -7 1.5 -13.5t-0.5 -13.5q0 -14 -14 -14
|
||||
q-10 0 -19.5 0.5t-19.5 1.5q-27 2 -54 0.5t-54 0.5q-18 2 -23 6.5t-5 21.5q0 56 -1 104.5t0 104.5q1 89 2.5 176.5t3.5 176.5v31q0 19 19 19q14 0 28.5 0.5t29.5 -0.5q20 -1 38.5 -1.5t38.5 -2.5t24.5 -5.5t4.5 -23.5q1 -48 1 -85.5t-1 -84.5q0 -18 0.5 -28.5t5 -15.5
|
||||
t14.5 -6t28 -1q32 0 62.5 1.5t61.5 2.5q20 0 20 17q-1 5 -0.5 10t0.5 10q-2 54 -3 98t-3 98q-2 16 12 16h123q11 0 16 -1q15 -2 19.5 -6.5t5.5 -20.5q3 -72 3 -143q0 -39 -1 -77.5l-2 -77.5v-24v-57.5t0.5 -71t0.5 -64.5z" />
|
||||
<glyph glyph-name="uni0415" unicode="Е" horiz-adv-x="594"
|
||||
d="M88 -1h-54q-22 -1 -25.5 3t-3.5 27q0 25 0.5 50t0.5 50v227t1 226q0 5 -0.5 8.5t-0.5 7.5q0 15 0.5 23.5t5 13t13 6t24.5 1.5h38t71 -0.5t88 -1t89.5 -0.5h75t44.5 1q21 0 26.5 -6.5t5.5 -24.5q0 -26 -0.5 -49.5t0.5 -48.5q0 -22 -21 -22q-12 0 -23.5 1t-23.5 1h-147h-81
|
||||
q-15 0 -19 -4t-3 -19q1 -20 2 -36t2 -36q1 -22 22 -22h19.5h26h26h19.5q23 2 21 -20v-66q0 -11 -16 -13q-7 0 -19.5 -0.5t-26 -1.5t-26 -1.5t-19.5 -0.5q-11 -1 -16 -5.5t-5 -15.5v-78q-2 -21 21 -21q11 0 21 0.5t21 0.5q40 -1 79.5 -1t79.5 -1q16 0 31 0.5t31 0.5
|
||||
q14 0 19.5 -4t6.5 -17q2 -26 3.5 -51t1.5 -51q0 -21 -6.5 -27t-27.5 -5h-41.5h-63h-75.5t-76.5 0.5t-67.5 0.5t-48 1z" />
|
||||
<glyph glyph-name="question" unicode="?" horiz-adv-x="552"
|
||||
d="M231 184q-14 0 -27 -0.5t-27 0.5t-19.5 7t-5.5 20q0 31 10 58t35 47q18 15 37 27t42 20q21 7 30 22.5t13 35.5q6 36 -24 57q-8 6 -13 7q-17 4 -38 3t-40.5 -7.5t-35.5 -18.5t-23 -29q-3 -6 -5 -12.5t-13 -8.5q-11 -3 -20 -3.5t-20 6.5q-18 13 -36.5 24l-37.5 22
|
||||
q-11 6 -10.5 11.5t4.5 13.5q32 63 82 102t121 52q39 7 76.5 4t75.5 -14q42 -12 68 -41.5t40 -69.5q16 -44 10.5 -85.5t-24.5 -81.5q-6 -13 -17.5 -23t-22.5 -18t-23 -14t-23 -13q-23 -13 -39 -31t-21 -44q-3 -14 -10.5 -18.5t-18.5 -4.5q-7 0 -15 -0.5t-16 -0.5
|
||||
q-9 -1 -19 -1zM316 56q0 -14 -2 -22.5t-7 -14.5t-13 -11t-21 -10q-31 -11 -63.5 -2t-48.5 36q-11 20 -8 40q4 33 31 53t63 17q31 -2 50.5 -26.5t18.5 -59.5z" />
|
||||
<glyph glyph-name="exclam" unicode="!" horiz-adv-x="270"
|
||||
d="M157 636q13 -1 21.5 -2.5t12.5 -5.5t5 -12t1 -21q-1 -26 -3 -51.5t-5 -50.5l-15.5 -125.5l-15.5 -125.5q-1 -5 -1 -9.5t-1 -9.5q-3 -17 -8 -21t-23 -4q-21 0 -42 1t-42 1q-15 0 -15 12q-2 21 -3 41.5l-2 40.5q-4 78 -7 152t-7 152q0 4 -0.5 7.5t-0.5 7.5q1 14 5 19t18 4
|
||||
h27h38h37.5h25.5zM87 0q-34 1 -58 22t-23 50q0 18 7.5 33.5t19.5 27.5t27.5 19t32.5 7q16 0 31 -6.5t25.5 -17.5t16 -25.5t4.5 -30.5q-2 -15 -7.5 -29.5t-15.5 -26t-24.5 -18t-35.5 -5.5z" />
|
||||
<glyph glyph-name="emdash" unicode="—" horiz-adv-x="510"
|
||||
d="M390 301q15 0 18 -2.5t4 -16.5q2 -17 2 -32.5t1 -32.5q0 -18 -16 -19l-30 -2t-30 -1q-26 0 -72 -1t-92.5 -2.5t-84 -2.5t-47.5 -2q-19 -1 -28 3t-10 25q-2 20 -0.5 34.5t-0.5 31.5q-2 11 2.5 14.5t16.5 4.5h38h63h76.5h77.5t67 0.5t45 0.5z" />
|
||||
<glyph glyph-name="endash" unicode="–" horiz-adv-x="375"
|
||||
d="M154 296h120q13 0 15 -2t1 -16q0 -16 0.5 -31.5t0.5 -32.5q-1 -14 -5 -17.5t-18 -5.5h-11l-219 3q-19 0 -23.5 4t-6.5 23q-2 14 -1.5 27t0.5 26q-1 13 2.5 17t17.5 4q32 1 63.5 0.5t63.5 -0.5v1z" />
|
||||
<glyph glyph-name="uni0430" unicode="а" horiz-adv-x="760"
|
||||
d="M259 135q-11 0 -16.5 -1t-9 -3.5t-6 -8.5t-5.5 -16q-5 -18 -11 -35.5t-11 -35.5q-7 -34 -40 -34q-36 -2 -71.5 -2t-71.5 -1q-16 0 -11 16l47.5 144.5l46.5 145.5q14 44 26 87.5t26 87.5l20.5 60t19.5 61q6 20 18 28t30 8q21 -1 41.5 -1.5t40.5 -0.5q23 0 46 0.5t46 0.5
|
||||
q13 0 25.5 0.5t25.5 0.5q12 0 19 -1t11 -4t6.5 -9.5t5.5 -18.5l40 -155.5l41 -155.5l32.5 -116l33.5 -115q6 -24 8.5 -36.5t-1 -18.5t-14 -6.5t-30.5 0.5q-34 0 -67.5 1l-67.5 2q-18 1 -21 18q-2 15 -5 30.5l-6 30.5q-2 8 -4 15t-4 15q-5 20 -24 20q-13 -1 -25.5 -0.5
|
||||
t-24.5 0.5l-12 0.5t-29 1t-36 1t-32 0.5zM360 481q-2 3 -6 -6.5t-6 -12.5l-27 -81l-23 -65l-23 -66q-5 -14 -1.5 -18t18.5 -4q24 -1 48 -1h48q19 1 22 4.5t-2 23.5q-8 41 -17 81l-18 81q-2 7 -3.5 18t-3.5 21l-3.5 17.5t-2.5 7.5z" />
|
||||
<glyph glyph-name="uni0431" unicode="б" horiz-adv-x="635"
|
||||
d="M156 -12q-31 1 -61.5 1t-62.5 2q-17 1 -22 6t-5 22q1 34 1.5 67.5t1.5 68.5v135v246q0 44 -3 89q0 11 4 14.5t14 3.5h38.5h60.5t71.5 -0.5t73 -0.5h65.5t47 1q11 1 21.5 1.5t21.5 -0.5q18 -1 25 -8t6 -26q-1 -25 -3 -47.5t-4 -47.5q-1 -8 -2 -13t-3.5 -7t-7.5 -2.5
|
||||
t-14 -0.5q-35 -1 -69.5 -0.5t-69.5 -0.5q-22 0 -45 -0.5t-44 -0.5q-16 0 -18 -16q-2 -17 -0.5 -33t0.5 -34q0 -9 5 -12t13 -3q28 -3 55.5 -2.5t56.5 0.5q29 -1 57 -7.5t56 -15.5q35 -12 60.5 -35t42.5 -55q14 -26 13 -51t-7 -62q-9 -56 -38 -90.5t-66.5 -53.5t-77.5 -25
|
||||
t-73 -6h-21.5h-39h-37t-16.5 -1zM233 256q-8 2 -17 1t-19 0q-20 2 -21 -17q0 -23 -0.5 -44.5t-0.5 -44.5q0 -17 18 -20q57 -12 110 11q14 6 22.5 17t11.5 24t-0.5 25.5t-14.5 22.5q-10 8 -22.5 12.5t-25.5 7.5q-9 3 -19.5 4t-21.5 1z" />
|
||||
<glyph glyph-name="uni0432" unicode="в" horiz-adv-x="646"
|
||||
d="M429 377q14 -5 25 -10l22 -10q9 -4 20 -16t20.5 -32t16 -48.5t6.5 -64.5q0 -29 -14 -58t-35.5 -54.5t-47.5 -45t-50 -28.5q-36 -14 -73.5 -19t-75.5 -6q-2 0 -4 0.5t-4 -0.5q-17 -2 -49.5 -1.5t-65.5 1.5q-39 1 -82 2q-7 0 -12 2t-9 5t-6 9q-5 17 -5 33q-1 79 -1 157.5
|
||||
v157.5q0 44 0.5 87t0.5 87q1 29 0.5 57.5t-2.5 57.5q0 15 12 15q64 1 120.5 2t120.5 0q23 0 45.5 -5t45.5 -11q29 -8 53 -25.5t44 -40.5q12 -15 17 -37q4 -20 6 -39.5t0.5 -37.5t-7.5 -35.5t-19 -33.5q-2 -3 -5 -6.5t-8 -8.5zM186 205v-26q-2 -20 -2 -30t4.5 -14t14.5 -4
|
||||
t28 -1q30 -2 57 8t51 27q40 27 14 70q-10 15 -28 27q-29 19 -59.5 19.5t-62.5 -3.5q-10 -2 -13 -7t-3 -14q0 -7 -0.5 -15.5t-0.5 -16.5v-20zM183 478v-41.5t6 -19.5t20 -4t43 4q12 2 23 6.5t22 10.5q14 8 20 21.5t5 29.5t-11 25.5t-24 13.5q-12 3 -24.5 4l-25.5 2
|
||||
q-12 1 -24 0t-23 -1q-2 -13 -3.5 -25.5t-3.5 -25.5z" />
|
||||
<glyph glyph-name="uni0433" unicode="г" horiz-adv-x="542"
|
||||
d="M8 293v50t-0.5 61l-1 64t-1 58.5t-1 44.5t-0.5 23v15t1 15q0 17 19 18q21 0 54.5 0.5t73 0.5h81h77.5t63.5 -0.5t38.5 -0.5q18 0 22 -4t6 -22q3 -31 2 -58.5t2 -58.5q2 -16 -2 -21.5t-19 -7.5q-6 -1 -33 -1.5t-61.5 -0.5t-69 -0.5t-56.5 -0.5q-16 0 -16 -17
|
||||
q0 -4 -0.5 -7.5t-0.5 -7.5q1 -75 0 -156t1 -156q1 -23 0.5 -40t0.5 -40q1 -14 -2 -31q-3 -15 -19 -15q-38 -2 -72.5 -2.5t-71.5 -1.5q-8 0 -10.5 4t-3.5 10q-1 7 -1 13.5v13.5v130t-1 129h1z" />
|
||||
<glyph glyph-name="uni0434" unicode="д" horiz-adv-x="792"
|
||||
d="M570 179q1 -20 4.5 -24.5t23.5 -5.5q13 -1 25.5 -1t24.5 -1q15 0 21 -4t9 -18q1 -6 1 -12v-11q0 -33 0.5 -66t0.5 -66v-11.5t-1 -11.5q-3 -17 -22 -17q-21 0 -42.5 -0.5t-42.5 -0.5q-20 0 -17 20v3q0 17 -1 26t-5 13.5t-13 5.5t-25 1q-12 0 -42 0.5t-69 1t-82.5 1
|
||||
t-82 0.5h-67.5t-39 -1q-4 -1 -8 -6t-5 -10q-2 -11 -1.5 -27t0.5 -27q0 -6 -2.5 -11t-10.5 -5q-26 -1 -55 -2t-55 0q-14 0 -18 5t-5 19q-2 44 -1 87t-1 87q-1 14 2.5 17.5t17.5 1.5q22 -3 36 16l27.5 35t24.5 37q22 39 39.5 79.5t27.5 84.5q2 9 8 31.5t12 49t10 51.5t4 40
|
||||
q-1 20 1.5 36.5t3.5 36.5q1 19 20 18q49 0 97 0.5t96 -0.5q72 0 143 -3q17 -1 20.5 -5t3.5 -21q0 -19 0.5 -53.5t1.5 -77l2 -88.5t1.5 -88t1.5 -76.5t2 -52.5zM400 314v18t-1 44t-1.5 55t-1.5 51q0 5 -2 11t-4 6q-11 1 -22.5 1t-22.5 -2q-9 -2 -8.5 -9t-0.5 -14
|
||||
q-2 -16 -3 -29t-3 -30q-8 -65 -27.5 -128t-51.5 -121q-1 -3 -2 -5.5t-2 -5.5q-3 -6 -0.5 -9.5t8.5 -3.5q31 0 67.5 1t67.5 2q17 0 17 17v17.5t-1 17.5q-2 29 -3 58t-3 58z" />
|
||||
<glyph glyph-name="uni0435" unicode="е" horiz-adv-x="594"
|
||||
d="M88 -1h-54q-22 -1 -25.5 3t-3.5 27q0 25 0.5 50t0.5 50v227t1 226q0 5 -0.5 8.5t-0.5 7.5q0 15 0.5 23.5t5 13t13 6t24.5 1.5h38t71 -0.5t88 -1t89.5 -0.5h75t44.5 1q21 0 26.5 -6.5t5.5 -24.5q0 -26 -0.5 -49.5t0.5 -48.5q0 -22 -21 -22q-12 0 -23.5 1t-23.5 1h-147h-81
|
||||
q-15 0 -19 -4t-3 -19q1 -20 2 -36t2 -36q1 -22 22 -22h19.5h26h26h19.5q23 2 21 -20v-66q0 -11 -16 -13q-7 0 -19.5 -0.5t-26 -1.5t-26 -1.5t-19.5 -0.5q-11 -1 -16 -5.5t-5 -15.5v-78q-2 -21 21 -21q11 0 21 0.5t21 0.5q40 -1 79.5 -1t79.5 -1q16 0 31 0.5t31 0.5
|
||||
q14 0 19.5 -4t6.5 -17q2 -26 3.5 -51t1.5 -51q0 -21 -6.5 -27t-27.5 -5h-41.5h-63h-75.5t-76.5 0.5t-67.5 0.5t-48 1z" />
|
||||
<glyph glyph-name="uni0451" unicode="ё" horiz-adv-x="594"
|
||||
d="M475 634q19 0 21 -18q0 -4 0.5 -20t0.5 -34t-0.5 -33t-0.5 -16q-1 -14 -4.5 -18t-18.5 -4q-16 0 -52.5 -0.5t-78 -1t-78.5 -1.5t-54 -1q-15 0 -20.5 -5.5t-6.5 -19.5q-2 -20 -2 -32v-31q0 -20 23 -20q21 0 41.5 -1l41.5 -2q19 -2 15 -21q-2 -14 -1.5 -28.5t-1.5 -28.5
|
||||
q-2 -17 -7.5 -21.5t-22.5 -4.5t-33 0.5t-33 0.5q-14 -1 -19 -5t-5 -18q1 -20 0 -40.5t1 -40.5q2 -17 7.5 -20.5t21.5 -2.5q27 0 56 0.5t56 0.5q34 -1 68 -1.5t68 -2.5q18 -1 24 -7t5 -23v-24.5t0.5 -28.5t0.5 -24.5v-12.5q-1 -16 -6.5 -21t-22.5 -5h-40h-75.5h-93.5h-93.5
|
||||
h-76h-40.5q-13 0 -20 0.5t-10.5 4t-4.5 10.5t-1 19q-1 61 -1 120t-1 120v89q1 43 2 78.5t2 78.5q1 24 0 48t0 48q0 17 3.5 20.5t20.5 3.5q47 1 93 0.5t93 -0.5l28.5 0.5t69 0t85 -1.5t77.5 -2zM127 695q-16 1 -29.5 7t-23.5 16.5t-15.5 23.5t-4.5 28q1 13 7.5 25t16.5 21
|
||||
t22.5 14.5t25.5 5.5q12 0 24.5 -6.5t22 -17t15.5 -23t6 -24.5q0 -15 -5.5 -28t-14.5 -22.5t-21.5 -15t-25.5 -4.5zM396 831q12 0 23.5 -5t20.5 -13.5t14 -19t5 -22.5q-1 -30 -18.5 -49.5t-44.5 -18.5q-29 0 -48 19.5t-19 49.5q-1 26 18 42.5t49 16.5z" />
|
||||
<glyph glyph-name="uni0436" unicode="ж" horiz-adv-x="927"
|
||||
d="M352 369q2 0 3 12.5t2 29.5t1 36t-1 33q-2 38 -4.5 69.5t-4.5 69.5q-1 17 2.5 21t20.5 5q32 1 64 0.5t64 -0.5q11 0 13.5 -5t2.5 -15q0 -61 1 -116t2 -116v-8t2 -7q1 -2 4.5 -3t3.5 0q5 5 9 10t7 11l57.5 108t57.5 109q7 15 16.5 23t27.5 6q25 -2 50 -1t50 1q9 0 17 -1
|
||||
t13 -12q4 -10 1 -17t-8 -15l-74.5 -114.5l-74.5 -114.5q-9 -15 -9 -29t8 -29l24 -46l30 -57l31.5 -60.5l28.5 -56l22 -44t12 -23.5q9 -19 -11 -22q-17 -3 -34 -3q-23 2 -46.5 1t-46.5 1q-19 1 -27 18l-59 114.5l-60 114.5q-2 3 -3.5 6.5t-3.5 7.5q-3 3 -6 3q-4 0 -4 -5
|
||||
q-1 -7 -1.5 -13.5t-0.5 -13.5q2 -57 3 -111l2 -112q1 -10 -5 -16.5t-15 -3.5q-32 -1 -62 0.5t-61 -0.5q-17 -2 -20.5 2.5t-4.5 21.5l-2 114.5l-2 115.5v9.5t-1 9.5q0 5 -5 5q-5 2 -7 -2l-5.5 -7.5t-5.5 -8.5l-48.5 -81l-49.5 -82l-28 -44l-28 -44q-5 -8 -9.5 -9t-22.5 0
|
||||
q-3 2 -18 2.5t-34.5 0.5h-39h-30.5q-29 2 -12 25q8 13 17 24t17 24l27.5 41.5l31.5 48.5l32 49.5l29 44.5l21 32.5t9 13.5q14 20 2 40l-21.5 39l-21.5 39q-23 38 -45 69t-45 69q-11 18 -21 36.5t-21 36.5q-10 17 11 17q13 0 33.5 -0.5t39.5 -0.5t33.5 -0.5t16.5 -0.5
|
||||
q10 2 22 1.5t18 -4.5q9 -6 14.5 -17l10.5 -21l30 -55.5l30 -52l30 -51.5l31 -55q2 -3 4 -9t7 -6z" />
|
||||
<glyph glyph-name="uni0437" unicode="з" horiz-adv-x="593"
|
||||
d="M403 626q44 -21 65.5 -57.5t23.5 -75.5t-13.5 -74.5t-45.5 -54.5q-6 -4 -9 -7q-4 -4 -4 -6.5t6 -6.5q11 -7 21 -15l20 -16q18 -16 28 -41.5t12 -53.5t-4 -54.5t-19 -44.5q-39 -54 -90 -83.5t-115 -38.5q-16 -2 -31.5 -3t-30.5 -1q-29 -2 -54.5 4t-49.5 16.5t-47.5 23.5
|
||||
l-46.5 27q-8 5 -11.5 13.5t0.5 18.5l22.5 52l22.5 52q2 4 8 6t11 1t9.5 -4.5t9.5 -5.5q35 -20 72.5 -34t79.5 -17q21 -2 38 6l34 16q5 3 8.5 10t5 15.5t1.5 17.5t-3 15q-17 42 -60 48q-14 2 -27.5 2t-27.5 1q-16 0 -16 15q-2 18 -3.5 36.5t-3.5 36.5q18 5 35.5 8.5t35.5 8.5
|
||||
l24 8t23 12q14 10 20.5 23t5 26t-10 24.5t-24.5 19.5q-20 11 -41 13t-44 -6t-45 -16.5t-43 -20.5q-19 -11 -27 -9.5t-20 21.5q-13 20 -25 40.5t-23 41.5q-2 4 -1 10t4 10q5 5 11 9t13 7q44 24 87 32t91 20q6 2 24.5 3t43 -0.5t51 -6.5t49.5 -17z" />
|
||||
<glyph glyph-name="uni0438" unicode="и" horiz-adv-x="750"
|
||||
d="M7 616q0 18 4.5 21.5t23.5 1.5q20 -2 40.5 -1.5t40.5 -0.5q12 0 24.5 1t25.5 -1q15 -2 20.5 -8.5t5.5 -22.5q1 -5 0.5 -9.5t-0.5 -9.5v-264v-13.5t1 -13.5q0 -7 5 -9q6 0 9 5q4 3 7 7t5 8q51 71 100.5 148.5t99.5 147.5q12 20 28 28.5t43 7.5q26 -3 53.5 -2t53.5 0
|
||||
q24 -2 31 -9t6 -31q-2 -102 -1.5 -209.5t-1.5 -209.5q-1 -39 0 -73.5t2 -73.5q0 -7 0.5 -12t-0.5 -12q-1 -9 -6 -14.5t-15 -3.5q-32 1 -65 2.5t-66 0.5q-7 0 -14 0.5t-13 3.5q-4 2 -7.5 7t-3.5 10q2 37 0 80t0 80q1 37 1 73.5t1 73.5q0 2 -1 6t-5 4q-3 0 -4.5 -2t-3.5 -4
|
||||
q-48 -59 -96 -127.5t-88 -134.5l-15.5 -24l-15.5 -24q-9 -15 -22 -19t-29 -6q-12 -1 -23.5 -0.5t-23.5 0.5q-20 1 -40.5 -0.5t-40.5 0.5q-23 2 -27.5 8t-3.5 30q2 37 2.5 73.5t0.5 73.5q0 38 -1 75.5l-2 75.5l0.5 15t0.5 39v55v63.5v63.5v55z" />
|
||||
<glyph glyph-name="uni0439" unicode="й" horiz-adv-x="708"
|
||||
d="M6 621q0 16 6 23t21 5q35 -2 67.5 0t67.5 -2q22 -2 20 -23v-35.5v-61.5t0.5 -73.5t1 -71t1.5 -53.5t2 -21q11 0 15 6q14 17 26.5 34l25.5 34l45.5 65l45.5 65l39.5 55l39.5 56q5 7 8.5 11t9 6.5t13 3t21.5 0.5h78q16 0 24 -0.5t12 -3.5t4.5 -10.5t1.5 -20.5
|
||||
q1 -25 1.5 -73t0.5 -106.5v-121t-0.5 -115t-0.5 -89.5v-45q0 -12 0.5 -20.5t-1.5 -20.5q-2 -13 -7 -17t-19 -3q-26 2 -52.5 0.5t-52.5 0.5q-9 1 -21.5 0t-15.5 2t-2 19t0 27q0 44 0.5 79t-0.5 79q0 23 -0.5 46.5t-1.5 46.5q0 7 -4 10q-4 2 -9 -3q-4 -4 -10 -12l-79 -103.5
|
||||
l-79 -103.5q-14 -20 -28.5 -37t-28.5 -37q-10 -15 -29 -15q-35 2 -69.5 1t-69.5 1q-8 0 -10.5 4t-3.5 11v13.5t-1 13.5q0 70 -0.5 139.5t-1.5 140.5q-1 0 -1 21v52.5v69v71v57.5v29zM308 710q-15 0 -29 6t-24 15.5t-16 21.5t-5 25q2 29 25 48.5t55 18.5q65 -1 68 -72
|
||||
q1 -12 -5.5 -23.5t-17 -20.5t-24 -14t-27.5 -5z" />
|
||||
<glyph glyph-name="uni043A" unicode="к" horiz-adv-x="677"
|
||||
d="M7 611q0 15 4 19.5t18 3.5q24 0 45 -1t46 -2q5 -1 11 -1h12q20 0 24 -3.5t5 -23.5q2 -32 2.5 -64t1.5 -64q1 -6 0 -11.5t0 -11.5l2 -10.5t3 -10.5q5 2 9 4.5t7 5.5l89 86.5l88 87.5q11 10 22 16.5t25 5.5q38 -1 75.5 -1.5t75.5 -1.5q3 0 7 -3t5 -5q1 -3 -1 -7.5t-4 -6.5
|
||||
l-76 -77.5l-76 -77.5q-14 -14 -28.5 -26.5t-28.5 -26.5q-14 -13 -14.5 -18t9.5 -21l41.5 -67.5l42.5 -67.5l61 -96.5l61 -96.5q8 -12 3.5 -22t-18.5 -12q-5 -1 -11 -1h-12q-21 -1 -42.5 -1t-42.5 -2q-23 -2 -39.5 6.5t-29.5 26.5q-14 20 -31 47t-31 47l-42 60.5l-42 60.5
|
||||
q-8 12 -13.5 12.5t-16.5 -9.5q-20 -16 -19 -44q2 -45 2.5 -89t1.5 -89q1 -20 -3.5 -25.5t-23.5 -5.5q-30 0 -64.5 1t-64.5 3q-23 1 -24 25q-1 18 -1 63v103.5v124v124.5v105v65z" />
|
||||
<glyph glyph-name="uni043B" unicode="л" horiz-adv-x="750"
|
||||
d="M635 356q0 -48 -0.5 -95t0.5 -95q1 -42 2 -76t3 -76q1 -17 -13 -17q-35 1 -70 1.5t-70 1.5q-10 0 -12 5t-2 13v54.5t-1 55.5l-3 213q-1 25 0 50t0 51q0 11 -1 17.5t-4.5 9.5t-10 4t-18.5 2h-8q-10 0 -15.5 -0.5t-9 -2.5t-6 -7.5t-5.5 -14.5l-23.5 -84t-25.5 -83
|
||||
q-15 -45 -32 -89.5t-39 -86.5q-12 -23 -31.5 -42.5l-39.5 -38.5q-17 -17 -39 -23.5t-46 -7.5q-33 -2 -64 5t-61 18q-14 5 -17.5 11.5t-1.5 21.5q6 29 11.5 59t12.5 59q4 21 25 15l9 -3.5l9 -3.5q31 -11 59.5 -2.5t46.5 35.5q8 12 14.5 25t10.5 27l27 91.5t24 91.5
|
||||
q11 42 19 85t17 85q3 17 7.5 21.5t21.5 6.5q4 0 8 0.5t7 -0.5q17 0 43 0.5t56.5 0.5h62.5h61h52h35q18 -1 21.5 -5t3.5 -23q0 -47 -0.5 -93t-1.5 -93v-21.5t0.5 -24t1 -20z" />
|
||||
<glyph glyph-name="uni043C" unicode="м" horiz-adv-x="979"
|
||||
d="M5 622q0 12 3.5 16t16.5 5q15 1 40.5 1t51 -1t45 -1t24.5 1q18 4 25 -12q28 -58 52.5 -103l47 -84.5l44.5 -77.5l45 -81q8 -15 17 -26q7 -11 15 0l102 165l23.5 40l31.5 56l29 53.5l17 32.5q16 36 55 33q30 -2 60 -2.5t60 -1.5q10 0 19 -3q8 -2 13 -6.5t5 -14.5
|
||||
q0 -41 1 -81.5l2 -81.5q2 -66 2.5 -132t2.5 -132l2 -81l2 -82q1 -18 -7 -25t-25 -6l-105 3q-12 0 -19 1t-10 4t-3.5 9t-0.5 18q0 68 -0.5 131.5t-0.5 132.5v9.5t-1 9.5t-1.5 11t-4.5 6q-3 1 -9.5 -7.5t-8.5 -10.5l-85.5 -128.5t-78.5 -131.5q-2 -3 -3.5 -6.5t-3.5 -6.5
|
||||
q-10 -16 -15 -25t-11 -12.5t-16 -4t-30 -0.5q-30 1 -59 0q-26 -3 -34 23l-5.5 15t-5.5 14q-11 25 -22 46t-22 46l-16.5 35.5l-16.5 35.5q-6 13 -11.5 26.5t-12.5 26.5q-5 11 -13.5 26t-12.5 15q-1 0 -1.5 -14.5t-0.5 -38v-53.5t0.5 -60l1 -57.5t0.5 -47.5v-11t-1 -12
|
||||
q-3 -21 -24 -19q-32 1 -63.5 0t-63.5 1q-18 1 -22.5 5.5t-5.5 21.5q-1 29 -1 53t-1 53q-1 39 -1.5 77.5t-0.5 77.5q0 13 0.5 25.5t0.5 24.5v15v39.5v56v64.5t-0.5 64t-0.5 56z" />
|
||||
<glyph glyph-name="uni043D" unicode="н" horiz-adv-x="667"
|
||||
d="M554 94q1 -26 1.5 -41.5t0.5 -41.5q0 -11 -4.5 -14.5t-17.5 -2.5q-29 1 -58 1.5t-58 0.5q-17 0 -20.5 3t-4.5 20q-2 47 -4.5 96t-4.5 96v11t-1 12q0 12 -15 14q-40 2 -79.5 0.5t-78.5 0.5q-20 1 -18 -19q1 -49 -1 -95t0 -99q1 -7 1.5 -13.5t-0.5 -13.5q0 -14 -14 -14
|
||||
q-10 0 -19.5 0.5t-19.5 1.5q-27 2 -54 0.5t-54 0.5q-18 2 -23 6.5t-5 21.5q0 56 -1 104.5t0 104.5q1 89 2.5 176.5t3.5 176.5v31q0 19 19 19q14 0 28.5 0.5t29.5 -0.5q20 -1 38.5 -1.5t38.5 -2.5t24.5 -5.5t4.5 -23.5q1 -48 1 -85.5t-1 -84.5q0 -18 0.5 -28.5t5 -15.5
|
||||
t14.5 -6t28 -1q32 0 62.5 1.5t61.5 2.5q20 0 20 17q-1 5 -0.5 10t0.5 10q-2 54 -3 98t-3 98q-2 16 12 16h123q11 0 16 -1q15 -2 19.5 -6.5t5.5 -20.5q3 -72 3 -143q0 -39 -1 -77.5l-2 -77.5v-24v-57.5t0.5 -71t0.5 -64.5z" />
|
||||
<glyph glyph-name="uni043E" unicode="о"
|
||||
d="M654 323q-2 -46 -20 -97.5t-51 -101.5q-35 -51 -83 -82t-103 -52q-16 -6 -34.5 -10t-37.5 -5.5t-37.5 -0.5t-34.5 5q-18 4 -46 15.5t-60 29t-64 41.5t-58 54q-23 27 -34.5 59.5t-13.5 68.5q-3 54 4.5 106t30.5 102q12 26 32 54.5t51 54.5t74 48t102 36q29 6 65.5 5
|
||||
t74.5 -11q28 -7 56.5 -22t53.5 -33t44 -36.5t30 -32.5q31 -40 44.5 -86t14.5 -109zM302 154q27 -1 48.5 2.5t41.5 14.5q51 32 73 79t11 106q-5 29 -20.5 53t-38.5 41t-50.5 25t-56.5 4t-55.5 -12.5t-45.5 -31.5q-37 -42 -51 -91.5t1 -106.5q3 -10 10.5 -20t15.5 -17
|
||||
q27 -22 56.5 -35t60.5 -11z" />
|
||||
<glyph glyph-name="uni043F" unicode="п" horiz-adv-x="688"
|
||||
d="M561 353q-2 -58 -3 -115l-2 -114q-1 -26 0.5 -47.5t2.5 -47.5q0 -15 -3.5 -19.5t-17.5 -5.5l-65.5 -4t-65.5 -3q-19 -1 -19 23q0 38 0.5 70.5t0.5 70.5q0 63 0.5 124.5t-0.5 123.5q0 27 -1 39t-6 16.5t-17 4t-34 0.5q-29 1 -58 1.5t-58 -0.5q-19 0 -23.5 -4.5t-4.5 -21.5
|
||||
v-175q1 -58 1.5 -116t1.5 -116q0 -5 -1 -8q-1 -13 -2 -20t-5 -10t-11 -3.5t-20 -0.5q-28 1 -56 1.5t-56 3.5q-22 2 -27 8.5t-5 28.5v450v133q0 14 3 16t18 1q48 -2 94.5 -1.5t94.5 0.5q66 -1 129 2t127 0q16 0 31 -0.5t31 -0.5q15 0 18.5 -3t3.5 -18v-93q0 -21 0.5 -50
|
||||
l1 -56t1.5 -45.5z" />
|
||||
<glyph glyph-name="uni0440" unicode="р" horiz-adv-x="615"
|
||||
d="M6 483q1 36 1.5 63.5t1.5 62.5q0 10 0.5 16t3.5 9t8.5 4t15.5 2q8 1 15.5 0.5t15.5 -0.5q11 0 23 0.5t23 0.5q7 0 23 0.5t34 2t34.5 2t23.5 0.5q20 -2 39 -3.5t38 -3.5q45 -5 81.5 -25t70.5 -47q5 -4 8.5 -8t8.5 -9q24 -22 35.5 -43.5t13.5 -44.5t-3 -48t-14 -55
|
||||
q-21 -67 -60 -104t-102 -56q-35 -11 -70.5 -13t-72.5 0q-23 2 -28 -2.5t-6 -27.5q-1 -34 -0.5 -68t0.5 -68q0 -20 -5 -25t-26 -4l-55.5 2l-56.5 2q-19 1 -20 23q-1 27 -1 54v54v202v17v41t0.5 50.5t1.5 46.5zM230 495q-8 0 -17 -0.5t-17 -0.5q-9 -1 -14.5 -4.5t-6.5 -13.5
|
||||
q0 -29 -0.5 -60t-1.5 -60q0 -15 16 -17q36 -4 70 0.5t66 21.5q18 10 28 26q8 12 10 28.5t-3 31.5t-16 26.5t-29 14.5q-11 2 -25 3.5t-27.5 2.5t-23 1h-9.5z" />
|
||||
<glyph glyph-name="uni0441" unicode="с" horiz-adv-x="688"
|
||||
d="M-20 334q0 53 15.5 96.5t40.5 77t55.5 59t61 42.5t57 27t42.5 13q94 20 179 -19q19 -8 37.5 -19.5l35.5 -22.5q11 -6 10.5 -10.5t-9.5 -13.5l-45.5 -41.5l-45.5 -41.5q-14 -13 -32 -6q-30 11 -58 9q-18 -1 -44.5 -11t-51 -30t-42 -51.5t-17.5 -75.5q0 -53 23 -84.5
|
||||
t54 -47.5t62.5 -19.5t49.5 -0.5q32 5 57 22.5t45 43.5l9 10.5t6 4.5t7 -1.5t11 -7.5l39 -25t39 -26q14 -10 16 -16.5t-5 -21.5q-15 -34 -39 -60t-55 -46q-36 -23 -74.5 -39.5t-83.5 -18.5q-92 0 -153.5 26t-100.5 65t-58.5 84.5t-28 84.5t-9 65z" />
|
||||
<glyph glyph-name="uni0442" unicode="т" horiz-adv-x="615"
|
||||
d="M468 637q7 -1 13.5 -1t13.5 -2q20 -3 25 -8t5 -26q0 -26 -0.5 -47t-1.5 -47q-1 -17 -5 -21t-20 -3q-12 0 -30.5 -0.5t-36.5 -0.5t-32.5 -0.5t-17.5 -0.5q-15 0 -23.5 -1t-12 -5t-4 -12t0.5 -23v-32.5t-1 -75.5t-1.5 -97.5t-1.5 -97.5t-1.5 -75t-0.5 -31q-3 -30 -32 -31
|
||||
h-21.5h-26t-22.5 0.5t-12 0.5q-16 0 -25 0.5t-13 4.5t-5 12.5t-1 24.5v33t0.5 60t1 77l1 83t0.5 77.5v61.5v27q1 17 -19 19q-13 0 -34 1t-41.5 1.5t-37 1.5t-19.5 1q-15 1 -20 6t-5 20v39t1 39v12q0 9 -1 19t4 16q3 5 13.5 3.5t19.5 -1.5q13 0 46.5 0.5t76.5 0.5t91 -0.5
|
||||
t91 -0.5t76 -0.5t45 -0.5z" />
|
||||
<glyph glyph-name="uni0443" unicode="у"
|
||||
d="M159 641q14 0 22 -1.5t11.5 -5t3.5 -11t-2 -19.5q-5 -29 -0.5 -56t12.5 -54q16 -53 53 -81t84 -45q13 -5 17.5 -2t11.5 18l24 59l23 60l21 56l21 56q5 18 21 18l70 2t70 0q23 0 30 -9t-1 -32q-21 -62 -45 -120q-35 -94 -79 -181.5t-94 -173.5q-16 -28 -38 -49t-49 -38
|
||||
q-28 -17 -57.5 -30.5t-64.5 -16.5q-20 -1 -40 -0.5t-40 7.5q-28 9 -55.5 17t-52.5 25q-15 10 -16.5 14.5t5.5 22.5q10 28 18.5 55.5t19.5 54.5q8 22 18.5 24.5t30.5 -11.5q11 -8 22.5 -14.5t24.5 -11.5q38 -14 74 2t57 47q11 14 -7 20l-39 16l-39 16q-38 17 -68.5 42.5
|
||||
t-56.5 54.5q-27 31 -40.5 68t-20.5 77q-5 29 -8 54.5t-6 55.5q-1 14 2 17.5t17 3.5h28.5h40.5t39.5 -0.5t26.5 -0.5z" />
|
||||
<glyph glyph-name="uni0444" unicode="ф" horiz-adv-x="812"
|
||||
d="M457 646q4 -3 5 -14t1.5 -24t0.5 -23.5t2 -12.5q2 -5 7.5 -6l10.5 -2q88 -8 144 -38t76 -71q19 -41 30 -84q16 -66 -4 -121.5t-61 -103.5q-29 -35 -70 -53.5t-87 -24.5q-17 -3 -25.5 -5t-13 -6.5t-5 -13.5t-0.5 -25v-16q0 -18 -19 -21q-33 -3 -65.5 -3.5t-65.5 6.5
|
||||
q-20 3 -20 23q-1 14 -2.5 28.5t-2.5 28.5q-14 3 -28 5.5t-28 5.5q-20 5 -39.5 9t-39.5 10q-26 9 -50.5 23t-45 35t-34.5 49t-20 64q-10 57 4.5 107.5t45.5 97.5q17 25 40 42t51 28q29 12 60.5 19t63.5 8q10 0 16 1t8.5 4t3 9.5t0.5 17.5v15q-3 27 5.5 33.5t33.5 8.5
|
||||
q6 1 23.5 0.5t37 -1.5t36 -3.5t20.5 -5.5zM298 337h1q0 17 0.5 33t0.5 33q-1 17 -4 22t-21 3q-9 0 -26.5 -8.5t-34.5 -23t-29.5 -33.5t-11.5 -41q1 -32 13.5 -53.5t28 -34.5t32 -18t27.5 -5q9 1 15.5 3t7.5 16q0 27 0.5 53.5t0.5 53.5zM462 225q0 -17 15 -14q24 5 45.5 14.5
|
||||
t35.5 29.5q20 26 21.5 56t-9.5 61q-11 26 -32 35t-44 14q-2 0 -8 2q-14 2 -18 -0.5t-5 -16.5q0 -11 -0.5 -36t-0.5 -53.5v-54v-37.5z" />
|
||||
<glyph glyph-name="uni0445" unicode="х" horiz-adv-x="770"
|
||||
d="M649 -3q-47 -2 -90.5 -1t-89.5 -1q-11 0 -17 4t-11 12q-14 18 -27.5 39t-27.5 40l-22.5 30.5l-22.5 30.5q-14 19 -19 19t-18 -19l-31.5 -47l-31.5 -48l-8 -10.5t-7 -11.5q-26 -54 -88 -48q-32 -1 -54.5 0t-48.5 1q-10 0 -22 5q-12 3 -5 15l48.5 73l48.5 73l49 75l50 74
|
||||
q8 11 7 19.5t-8 17.5l-44.5 62.5l-43.5 62.5l-49.5 74l-49.5 74q-2 5 -4 9t-4 9q-3 12 10 12q41 1 81.5 1.5t81.5 1.5q8 0 12 -3.5t8 -9.5l50 -69.5l50 -69.5q2 -3 5 -6t5 -6q11 -14 21 0q5 8 10.5 15.5l11.5 15.5l38 54t37 56q15 21 38 21q33 0 66 0.5t67 1.5q9 0 12 -5
|
||||
t2 -13q-3 -12 -10 -24l-55.5 -85l-54.5 -84l-28 -45.5l-29 -44.5q-6 -9 -3.5 -14.5t7.5 -12.5l48.5 -60.5l48.5 -61.5l54 -73.5l53 -74.5q5 -7 9 -14t8 -15q6 -12 3 -16.5t-16 -4.5z" />
|
||||
<glyph glyph-name="uni0446" unicode="ц"
|
||||
d="M6 611q-1 16 2.5 20t20.5 7q4 0 19.5 0.5t35.5 0.5t40 -0.5t32 -0.5q11 0 14.5 -4.5t3.5 -14.5q1 -28 1.5 -68t0.5 -85t-0.5 -90.5t-0.5 -84.5t-0.5 -67.5t0.5 -38.5q0 -22 24 -22h73.5t73.5 -1q20 0 18 20q-1 16 -1 47.5v72t-0.5 85.5t-0.5 87t-1 77.5t-2 56.5v10t1 10
|
||||
q3 15 19 15q30 -2 60 -2.5t60 -2.5q16 -2 19.5 -5.5t4.5 -20.5q1 -24 2 -58t1.5 -73t0.5 -78.5t0.5 -73.5t0.5 -59v-34q1 -11 1 -21.5t1 -21.5q1 -17 2 -25.5t5.5 -13t13 -5.5t25.5 -1q3 0 6 0.5t5 -0.5q18 -1 27 -2t12 -5t3 -12t1 -21q4 -43 5 -83t0 -87q0 -15 -6 -23.5
|
||||
t-21 -9.5q-26 0 -49 0.5t-49 -0.5q-22 0 -25 20q-2 14 -1 28.5t-1 28.5q-2 17 -17 18q-9 1 -19 1.5t-19 -1.5q-17 0 -46 -0.5t-63.5 -0.5t-71 -1t-69 -1t-58 -0.5t-37.5 -0.5h-31q-10 1 -21 0.5t-18 4.5q-5 4 -4.5 15t-0.5 21q-3 97 1 192t0 192l-2 68q-1 29 -1 61.5v60.5z
|
||||
" />
|
||||
<glyph glyph-name="uni0447" unicode="ч" horiz-adv-x="656"
|
||||
d="M550 471q-1 -107 -0.5 -213t-0.5 -213q0 -11 -0.5 -16.5l-1.5 -16.5q-2 -14 -6.5 -18t-18.5 -3q-30 2 -60 0t-60 2q-26 3 -26 36v117q0 14 -1 28.5l-2 29.5q-1 17 -17 9q-5 -2 -10.5 -5.5t-10.5 -5.5l-41.5 -16t-41.5 -14q-52 -14 -96.5 0t-85.5 44q-24 18 -39.5 42.5
|
||||
t-17.5 55.5l-4 67.5t-3 68.5v43t0.5 39l1 39t0.5 43q0 22 20 22q5 0 21.5 0.5t36.5 0.5h39h30q12 -1 19 -2t10 -4t3.5 -9.5t0.5 -17.5q-1 -42 -2.5 -74.5t-2.5 -73.5q0 -17 1 -33t5 -33q5 -24 21 -38.5t45 -14.5q50 -1 90 29q14 11 21.5 23.5t7.5 32.5q-2 48 -1 93t1 93v8
|
||||
q1 17 5.5 22t21.5 5h19h33.5h36h28.5q20 1 25 -5t7 -26q1 -17 1 -41.5v-46.5t-0.5 -37.5z" />
|
||||
<glyph glyph-name="uni0448" unicode="ш" horiz-adv-x="969"
|
||||
d="M255 -2q-51 1 -101 1.5t-101 1.5q-10 0 -15 1q-17 2 -22.5 8.5t-5.5 23.5q-2 69 -1 136t0 136q-1 55 -0.5 110t0.5 111q1 23 0 37t-2 37q0 7 1 13t2 13q5 15 20 14q10 -1 26 -1.5t32.5 -0.5t30 0.5t16.5 0.5q13 1 21 0t12.5 -4.5t6.5 -11t3 -20.5q5 -62 4 -118t1 -119
|
||||
q1 -42 -1 -86t0 -86q1 -14 4.5 -17.5t18.5 -4.5q25 -2 54 -0.5t53 0.5t29 3.5t4 27.5q0 38 -1 75.5t-1 75.5q0 54 0.5 106.5t0.5 106.5q0 16 1 26.5t-1 26.5q-2 20 16 20q34 -2 65.5 0t65.5 -1q14 -2 18 -7t4 -22q1 -87 1 -172.5v-172.5q0 -19 0.5 -37.5t1.5 -36.5
|
||||
q1 -14 5.5 -18t18.5 -4q27 -1 54 -0.5t55 0.5q17 0 17 16v55q-1 41 -0.5 81t-1.5 82q-2 50 -2.5 98.5t-2.5 98.5q0 4 -0.5 7.5t-0.5 8.5q0 11 4.5 15.5t16.5 4.5h60t61 -1q11 -1 17 -6t7 -22q3 -97 4.5 -193.5t-3.5 -194.5q-2 -45 -0.5 -87.5t1.5 -87.5q0 -31 -8.5 -39
|
||||
t-38.5 -8q-17 0 -59.5 -0.5t-97 -0.5h-115.5h-113.5t-90.5 0.5t-48 0.5z" />
|
||||
<glyph glyph-name="uni0449" unicode="щ" horiz-adv-x="1083"
|
||||
d="M340 617q0 15 3 19.5t18 4.5q30 1 60 0.5t60 -1.5q17 0 17 -20q0 -44 0.5 -84t-0.5 -84q0 -66 -0.5 -132t-0.5 -132q-1 -17 2.5 -21t20.5 -5q29 -1 58.5 -1h58.5q13 0 20 1t10.5 4.5t4 10.5t0.5 19q0 42 -0.5 83.5t-0.5 83.5q-1 61 -1 122v122q0 11 0.5 17.5t3.5 10
|
||||
t9 4.5t17 1q30 1 60 -0.5t60 -3.5q12 0 14 -15q2 -20 3.5 -40.5t1.5 -40.5q1 -80 0 -159t2 -159l1.5 -41t5.5 -17.5t17 -4t36 -0.5l36.5 -2t36.5 -3q19 -2 20 -19q0 -7 1.5 -13.5t2.5 -13.5q2 -47 2.5 -93t2.5 -93q0 -6 -1 -11.5l-2 -11.5q-3 -18 -21 -18q-27 1 -54 1t-54 1
|
||||
q-19 0 -24 5t-7 23q-5 33 -6 66q-1 11 -5.5 16t-15.5 5q-20 0 -39 0.5t-39 0.5q-105 1 -209 1.5t-210 1.5q-67 0 -133.5 0.5t-133.5 0.5h-12t-11 2q-11 2 -17 9t-3 18q-2 21 -2 39v34.5t0.5 32.5t0.5 33q-2 52 0 103t-1 103q-4 60 -3.5 113.5t1.5 113.5v16q1 17 4.5 21
|
||||
t20.5 3q17 0 34.5 -1t34.5 -1q15 -1 29.5 -1.5t28.5 0.5q19 0 19 -18q-1 -23 -1.5 -40t-0.5 -40q1 -69 1.5 -138t0.5 -138q1 -17 0.5 -33t-0.5 -33q0 -21 3 -25.5t23 -5.5l52.5 -4t52.5 -2q15 0 22.5 1t10.5 4.5t3.5 11t0.5 20.5q-1 52 -1 104t-1 105l0.5 22.5t0.5 54.5
|
||||
v67.5v62.5z" />
|
||||
<glyph glyph-name="uni044A" unicode="ъ" horiz-adv-x="667"
|
||||
d="M76 324q-1 39 0.5 77.5t-0.5 77.5q-2 18 -6 21.5t-23 4.5q-23 1 -37.5 0t-23 3t-11 16t-1.5 39q1 14 0 27t-1 27q1 14 5 18.5t18 5.5h37.5t63.5 -0.5l69 -1t54 -1.5q3 1 9.5 1t12.5 -2.5t10.5 -8.5t4.5 -16v-28v-39v-42v-38q0 -22 20 -22q37 0 73 -3q51 -5 94 -24.5
|
||||
t76 -58.5q32 -38 41 -81t-2 -91q-14 -60 -50.5 -100t-94.5 -63q-41 -16 -81.5 -19t-82.5 -5q-37 -2 -74 0l-74 4q-18 1 -21 4t-3 20q-1 48 -1.5 95t-1.5 95v12v28.5t0.5 35t0.5 32.5zM302 136q7 1 16.5 1.5t19.5 2.5q8 2 16.5 6t13.5 10q19 20 25.5 43t-2.5 50
|
||||
q-15 49 -65 58q-21 4 -42 6q-17 2 -21.5 -2t-4.5 -22q-2 -66 0 -132q0 -14 3.5 -17t19.5 -4h21z" />
|
||||
<glyph glyph-name="uni044B" unicode="ы" horiz-adv-x="823"
|
||||
d="M7 283q1 36 1.5 89l1 104t0.5 90v48q-1 18 3 22t23 5h23t40 -0.5l43 -1t32 -0.5q16 -1 19 -3t5 -18q2 -34 0.5 -67.5t0.5 -67.5q0 -13 1 -20.5t4 -11t10 -5t21 -3.5q10 -1 19.5 -1t19.5 -2q60 -10 105 -41t72 -88q18 -38 23 -77t-7 -81q-11 -40 -36 -74.5t-66 -50.5
|
||||
q-42 -17 -81.5 -20.5l-83.5 -7.5q-5 0 -24 -0.5t-44 -0.5t-50 -0.5t-40 -1.5q-23 -1 -28.5 4.5t-5.5 28.5q0 66 -0.5 130.5t-0.5 122.5zM194 167q0 -8 3 -13.5t11 -5.5q10 0 24.5 0.5t28.5 3.5t26.5 7.5t20.5 12.5t12.5 20t6 25t-0.5 26t-7 23t-18.5 18.5t-29.5 14t-33 8
|
||||
t-29 1.5q-14 -2 -15 -14v-127zM543 615q0 14 3.5 19.5t17.5 4.5h62t62 -2q17 -1 20.5 -5.5t3.5 -22.5q2 -42 2.5 -79t0.5 -79v-194t-2 -194q0 -30 0.5 -43.5t-5 -18t-20 -3.5t-44.5 1q-19 -1 -37 0t-37 1q-18 0 -24 5t-6 24v150.5t-1 151.5l1 30.5t1.5 74.5t1 93t0.5 86z
|
||||
" />
|
||||
<glyph glyph-name="uni044C" unicode="ь" horiz-adv-x="593"
|
||||
d="M146 -2q-30 1 -52.5 1.5t-42.5 1.5q-16 0 -31 3q-7 2 -11.5 6.5t-2.5 12.5q0 48 -0.5 98t0.5 99v61.5v77v81.5v76v60v34q0 5 -0.5 9.5t0.5 9.5q3 20 24 20q30 -2 60 -3t60 -3q14 -1 22 -2t11.5 -5t4.5 -12t1 -22q1 -34 0.5 -59.5t1.5 -59.5q0 -18 3 -22t20 -5
|
||||
q22 -2 49 -1.5t49 -1.5q84 -6 142 -64q48 -49 54 -122q5 -83 -38 -156q-22 -36 -53 -60.5t-73 -35.5q-52 -14 -103 -18t-95 1zM191 251q2 -10 2 -29q0 -9 0.5 -18.5t0.5 -19.5q1 -26 6.5 -32t31.5 -4q15 1 30 6q14 4 28 10t25.5 16t18.5 23.5t7 33.5q0 19 -8 31t-21.5 19.5
|
||||
t-30 10.5t-34.5 5q-1 1 -4 1q-19 2 -29.5 2t-15.5 -5t-6 -16.5t-1 -33.5z" />
|
||||
<glyph glyph-name="uni044D" unicode="э"
|
||||
d="M624 300q1 -40 -9 -80.5t-31 -78.5t-54 -62q-34 -25 -68.5 -44.5t-75.5 -32.5q-73 -23 -141.5 -11.5t-135.5 40.5q-52 22 -95 62q-5 5 -8.5 9.5t-0.5 12.5l27 65.5l27 64.5q5 13 17 19q18 9 34 -7q14 -14 29 -27t32 -24q49 -33 100 -32.5t103 23.5q41 18 55 64
|
||||
q6 20 -14 20h-30t-51 -0.5t-51 -0.5h-31h-17t-17 1q-20 0 -20 21q0 11 -2 21t-2 21q-1 21 6.5 28.5t28.5 8.5q9 0 34.5 0.5t55 0.5t55 0.5t34.5 0.5q14 2 16 5.5t-2 16.5q-10 29 -32.5 44t-49.5 23q-92 26 -173 -18q-17 -9 -24.5 -5.5t-15.5 16.5l-24.5 39l-23.5 39
|
||||
q-3 5 -6 9.5t-7 8.5q-10 12 -9.5 16t11.5 14q78 66 178 69q61 3 121 -6q56 -8 99.5 -37.5t78.5 -71.5q44 -52 64 -112.5t15 -132.5z" />
|
||||
<glyph glyph-name="uni044E" unicode="ю" horiz-adv-x="969"
|
||||
d="M184 23q-1 -16 -5 -20.5t-19 -4.5q-29 1 -59 1t-59 2q-13 0 -20 1t-10 4.5t-3.5 10t-0.5 18.5q-2 79 -0.5 157t-0.5 157q-1 32 -1.5 64.5t1.5 64.5q2 37 2 70.5t-2 71.5q-1 14 1.5 18t13.5 4q36 0 69 -0.5t69 -0.5q16 -2 16 -17q1 -14 1 -27v-27q1 -37 0 -70.5t1 -71.5
|
||||
q0 -19 3.5 -22t21.5 -5q14 -1 27 -0.5t26 -0.5q17 -1 23 16q5 11 9.5 22.5t9.5 22.5q34 78 95 121t142 61q66 15 123 -1.5t107 -58.5q35 -31 60.5 -69t37.5 -86q9 -36 15 -72t2 -74q-5 -44 -17 -83.5t-38 -76.5q-19 -26 -40 -50t-49 -41q-53 -32 -109.5 -41.5t-117.5 -6.5
|
||||
q-42 3 -79 18t-72 38q-33 22 -52.5 54t-30.5 69q-7 22 -11.5 44l-9.5 45q-4 17 -20 18q-18 1 -28 0t-15.5 -6.5t-7 -15.5t-1.5 -28q0 -11 0.5 -38t0.5 -57.5t0.5 -59t0.5 -41.5zM442 324q0 -6 -0.5 -12t-0.5 -11q0 -34 15 -59.5t36.5 -43t44.5 -26t38 -8.5q72 0 111 47.5
|
||||
t39 125.5q0 12 -2 33.5t-13.5 44t-34.5 41t-65 26.5q-61 10 -104.5 -22t-59.5 -90q-2 -6 -2.5 -13.5l-1 -15t-0.5 -12.5v-5z" />
|
||||
<glyph glyph-name="uni044F" unicode="я" horiz-adv-x="688"
|
||||
d="M571 34v-23q-2 -20 -21 -18h-127q-18 1 -24 7t-6 23v54.5t1 54.5q0 17 0.5 34.5t0.5 35.5q0 15 -3.5 18t-18.5 5q-30 6 -46 -20l-57.5 -85t-55.5 -86q-7 -12 -12.5 -19.5t-13 -12.5t-19.5 -7t-31 -2h-34t-38 1q-21 0 -44 1q-6 2 -10 5q-8 6 -1 16q14 20 30 39.5t31 40.5
|
||||
l48 67.5l48 68.5q9 14 8.5 18.5t-14.5 10.5q-35 16 -55 43.5t-34 60.5q-14 35 -18.5 70.5t6.5 73.5q5 18 16 32.5t25 26.5q49 41 113 57q45 11 87 11.5t82 1.5q17 -1 42.5 -1t49 0.5t40.5 0.5h19q21 -3 28 -11t7 -29v-48v-87t0.5 -111t0.5 -119v-111.5v-87.5zM407 438
|
||||
q-1 4 -1 12.5t-1 17.5q-2 15 -14 16q-15 1 -43.5 2.5t-56 -1t-48.5 -12t-22 -30.5q-1 -37 17 -53.5t41 -21.5t44.5 -3.5t29.5 0.5q22 -2 32.5 1t14.5 11t4.5 23t2.5 38z" />
|
||||
<glyph glyph-name="A" unicode="A" horiz-adv-x="708"
|
||||
d="M436 638q18 1 23.5 -3t9.5 -21q8 -33 14.5 -64t14.5 -64l34 -142l34 -141l15.5 -64.5l15.5 -64.5l6 -25.5l6 -24.5q6 -23 -16 -27h-7q-36 -2 -72 -0.5t-71 -0.5q-15 0 -18.5 5t-5.5 22q-3 44 -16 88q-2 10 -8.5 11t-11.5 1q-36 1 -72 0l-71 -2q-8 0 -12 -4t-6 -11
|
||||
q-6 -23 -12.5 -43t-12.5 -43q-3 -11 -7.5 -15.5t-18.5 -4.5h-31h-50t-47.5 0.5t-22.5 0.5q-24 2 -16 25l20 60l21 59l42 133.5l43 133.5l17 56l19.5 64l16.5 53t8 24q3 10 4.5 16t5 9t11 4t21.5 1q12 0 40.5 -0.5t60 -0.5h60.5h42zM361 243q16 1 12 17l-16.5 71l-16.5 71
|
||||
q-1 3 -3 6.5t-6 3.5t-4.5 -3t-1.5 -5l-22 -64.5l-22 -64.5q-5 -15 -7 -22.5t0.5 -10.5t10 -3h21.5l6.5 0.5t15 1t18 1t15.5 1.5z" />
|
||||
<glyph glyph-name="a" unicode="a" horiz-adv-x="708"
|
||||
d="M436 638q18 1 23.5 -3t9.5 -21q8 -33 14.5 -64t14.5 -64l34 -142l34 -141l15.5 -64.5l15.5 -64.5l6 -25.5l6 -24.5q6 -23 -16 -27h-7q-36 -2 -72 -0.5t-71 -0.5q-15 0 -18.5 5t-5.5 22q-3 44 -16 88q-2 10 -8.5 11t-11.5 1q-36 1 -72 0l-71 -2q-8 0 -12 -4t-6 -11
|
||||
q-6 -23 -12.5 -43t-12.5 -43q-3 -11 -7.5 -15.5t-18.5 -4.5h-31h-50t-47.5 0.5t-22.5 0.5q-24 2 -16 25l20 60l21 59l42 133.5l43 133.5l17 56l19.5 64l16.5 53t8 24q3 10 4.5 16t5 9t11 4t21.5 1q12 0 40.5 -0.5t60 -0.5h60.5h42zM361 243q16 1 12 17l-16.5 71l-16.5 71
|
||||
q-1 3 -3 6.5t-6 3.5t-4.5 -3t-1.5 -5l-22 -64.5l-22 -64.5q-5 -15 -7 -22.5t0.5 -10.5t10 -3h21.5l6.5 0.5t15 1t18 1t15.5 1.5z" />
|
||||
<glyph glyph-name="B" unicode="B" horiz-adv-x="636"
|
||||
d="M3 604q0 14 5 21t19 10q8 1 16 1h16q37 -2 75 0q51 1 101.5 -1.5t101.5 -8.5q31 -4 54.5 -18t42.5 -37q6 -7 10.5 -19.5t8.5 -25.5t6.5 -23t2.5 -11q4 -35 -9.5 -64.5t-36.5 -54.5q-9 -10 -8 -12.5t14 -8.5l27.5 -13.5t25.5 -15.5q46 -36 48 -96q1 -89 -54 -163
|
||||
q-8 -11 -20.5 -19.5l-24.5 -16.5q-18 -12 -38.5 -18t-42.5 -8q-17 -1 -44 -1.5t-58.5 -0.5h-64.5t-61.5 0.5t-49.5 1t-29 0.5q-20 1 -26.5 8.5t-6.5 27.5v283l0.5 31t0.5 75t-0.5 92.5t-0.5 84.5zM171 140q0 -18 17 -17q12 1 24 0.5t23 1.5q20 4 39 10.5t35 21.5
|
||||
q23 23 19.5 48.5t-32.5 39.5q-26 11 -54.5 14.5t-57.5 2.5q-16 0 -16 -19q0 -6 0.5 -20.5l1 -30.5l1 -30.5t0.5 -21.5zM170 424q0 -19 16 -18q17 1 34 1.5t34 2.5q15 3 28 10.5t16 25.5q8 37 -21 57q-34 25 -77 24q-20 0 -24 -4t-5 -24v-13t-0.5 -22t-0.5 -23v-17z" />
|
||||
<glyph glyph-name="b" unicode="b" horiz-adv-x="636"
|
||||
d="M3 604q0 14 5 21t19 10q8 1 16 1h16q37 -2 75 0q51 1 101.5 -1.5t101.5 -8.5q31 -4 54.5 -18t42.5 -37q6 -7 10.5 -19.5t8.5 -25.5t6.5 -23t2.5 -11q4 -35 -9.5 -64.5t-36.5 -54.5q-9 -10 -8 -12.5t14 -8.5l27.5 -13.5t25.5 -15.5q46 -36 48 -96q1 -89 -54 -163
|
||||
q-8 -11 -20.5 -19.5l-24.5 -16.5q-18 -12 -38.5 -18t-42.5 -8q-17 -1 -44 -1.5t-58.5 -0.5h-64.5t-61.5 0.5t-49.5 1t-29 0.5q-20 1 -26.5 8.5t-6.5 27.5v283l0.5 31t0.5 75t-0.5 92.5t-0.5 84.5zM171 140q0 -18 17 -17q12 1 24 0.5t23 1.5q20 4 39 10.5t35 21.5
|
||||
q23 23 19.5 48.5t-32.5 39.5q-26 11 -54.5 14.5t-57.5 2.5q-16 0 -16 -19q0 -6 0.5 -20.5l1 -30.5l1 -30.5t0.5 -21.5zM170 424q0 -19 16 -18q17 1 34 1.5t34 2.5q15 3 28 10.5t16 25.5q8 37 -21 57q-34 25 -77 24q-20 0 -24 -4t-5 -24v-13t-0.5 -22t-0.5 -23v-17z" />
|
||||
<glyph glyph-name="C" unicode="C" horiz-adv-x="687"
|
||||
d="M-17 288q0 64 16 120.5t47 108.5q14 23 32.5 40.5t41.5 31.5q55 34 114 49t124 9q39 -4 75 -18.5l71 -29.5q25 -10 7 -34l-40.5 -49l-41.5 -49q-7 -8 -15 -9.5t-19 0.5q-21 4 -42.5 5t-41.5 -2q-30 -3 -53.5 -16t-40 -33t-24.5 -46t-6 -55l4 -34.5t7 -33.5
|
||||
q9 -28 27.5 -45.5t49.5 -21.5q20 -3 40 -5t40 3q28 7 46 20.5t38 39.5q14 29 38 12l51 -34t50 -36q15 -11 11 -20.5t-15 -26.5q-6 -8 -26 -26.5t-47 -38t-57 -37t-56 -24.5q-33 -9 -66 -11t-68 2q-45 6 -85.5 22.5t-75.5 45.5q-6 5 -16.5 14.5t-22 22t-23 27.5t-18.5 32
|
||||
t-12.5 33t-10.5 33q-4 17 -4.5 33.5t-2.5 30.5z" />
|
||||
<glyph glyph-name="c" unicode="c" horiz-adv-x="708"
|
||||
d="M3 288q0 64 16 120.5t47 108.5q14 23 32.5 40.5t41.5 31.5q55 34 114 49t124 9q39 -4 75 -18.5l71 -29.5q25 -10 7 -34l-40.5 -49l-41.5 -49q-7 -8 -15 -9.5t-19 0.5q-21 4 -42.5 5t-41.5 -2q-30 -3 -53.5 -16t-40 -33t-24.5 -46t-6 -55l4 -34.5t7 -33.5
|
||||
q9 -28 27.5 -45.5t49.5 -21.5q20 -3 40 -5t40 3q28 7 46 20.5t38 39.5q14 29 38 12l51 -34t50 -36q15 -11 11 -20.5t-15 -26.5q-6 -8 -26 -26.5t-47 -38t-57 -37t-56 -24.5q-33 -9 -66 -11t-68 2q-45 6 -85.5 22.5t-75.5 45.5q-6 5 -16.5 14.5t-22 22t-23 27.5t-18.5 32
|
||||
t-12.5 33t-10.5 33q-4 17 -4.5 33.5t-2.5 30.5z" />
|
||||
<glyph glyph-name="D" unicode="D" horiz-adv-x="709"
|
||||
d="M4 576q0 21 1 32.5t6.5 17t16.5 7t32 2.5q11 0 44 0.5t72 0.5t76 -1t57 -3q24 -2 48 -4.5t47 -10.5q21 -7 41.5 -18t38.5 -25q30 -20 52.5 -53.5t38 -68.5t24.5 -66t12 -47q13 -72 -9 -131t-71 -110q-34 -35 -74 -57t-89 -29q-63 -9 -124.5 -12.5t-124.5 -1.5
|
||||
q-26 0 -50.5 2t-49.5 2q-11 -1 -13.5 4t-2.5 13q0 4 -0.5 7.5t-0.5 7.5q0 71 0.5 141.5t0.5 141.5l0.5 28t0.5 68.5v85v77.5zM202 315q0 -33 -0.5 -66t-1.5 -66q0 -20 20 -21q43 -4 82 6t74 36q17 13 30 28t18 36q7 28 4.5 56t-11.5 56q-8 21 -22 37.5t-36 25.5
|
||||
q-32 14 -64.5 16t-65.5 2q-20 0 -23.5 -4t-3.5 -24q-1 -15 -0.5 -35t0.5 -39v-44z" />
|
||||
<glyph glyph-name="d" unicode="d" horiz-adv-x="708"
|
||||
d="M4 576q0 21 1 32.5t6.5 17t16.5 7t32 2.5q11 0 44 0.5t72 0.5t76 -1t57 -3q24 -2 48 -4.5t47 -10.5q21 -7 41.5 -18t38.5 -25q30 -20 52.5 -53.5t38 -68.5t24.5 -66t12 -47q13 -72 -9 -131t-71 -110q-34 -35 -74 -57t-89 -29q-63 -9 -124.5 -12.5t-124.5 -1.5
|
||||
q-26 0 -50.5 2t-49.5 2q-11 -1 -13.5 4t-2.5 13q0 4 -0.5 7.5t-0.5 7.5q0 71 0.5 141.5t0.5 141.5l0.5 28t0.5 68.5v85v77.5zM202 315q0 -33 -0.5 -66t-1.5 -66q0 -20 20 -21q43 -4 82 6t74 36q17 13 30 28t18 36q7 28 4.5 56t-11.5 56q-8 21 -22 37.5t-36 25.5
|
||||
q-32 14 -64.5 16t-65.5 2q-20 0 -23.5 -4t-3.5 -24q-1 -15 -0.5 -35t0.5 -39v-44z" />
|
||||
<glyph glyph-name="E" unicode="E" horiz-adv-x="583"
|
||||
d="M9 431q0 38 -1.5 75.5t-3.5 75.5q0 5 -0.5 10.5t-0.5 11.5q0 17 4 25t27 7q26 -1 65.5 -1h83h88.5t82.5 0.5t64.5 0.5h36q20 0 21 -19q1 -26 1 -57v-57q0 -3 -4.5 -7t-7.5 -4q-14 -2 -27.5 -3.5t-27.5 -1.5q-49 -1 -95 -1t-95 -1q-17 0 -26.5 -1t-13.5 -5.5t-4 -14
|
||||
t1 -27.5q0 -10 -0.5 -13t0.5 -12q1 -12 2 -19t4 -10.5t10 -4.5t20 -2q10 -2 27.5 -1t28.5 0q12 -1 17.5 -3.5t7.5 -7.5t1.5 -13t-0.5 -20q0 -4 -0.5 -7.5t0.5 -7.5q0 -18 -18 -18q-30 -2 -59 0q-16 0 -25 -0.5t-12.5 -4.5t-4 -12.5t-0.5 -23.5v-74q1 -21 21 -21h243
|
||||
q18 2 25 -2.5t8 -22.5q2 -28 2.5 -62t-0.5 -62q0 -4 -4.5 -8.5t-8.5 -5.5q-6 -1 -27.5 -1h-52t-66.5 0.5l-71 1t-66 0.5h-50q-23 -1 -47 0l-47 2q-19 1 -24.5 6.5t-5.5 24.5v66t1 66q0 23 0.5 44.5t1.5 44.5q-1 0 -0.5 19.5t1 47t1 58t0.5 52.5z" />
|
||||
<glyph glyph-name="e" unicode="e" horiz-adv-x="583"
|
||||
d="M9 431q0 38 -1.5 75.5t-3.5 75.5q0 5 -0.5 10.5t-0.5 11.5q0 17 4 25t27 7q26 -1 65.5 -1h83h88.5t82.5 0.5t64.5 0.5h36q20 0 21 -19q1 -26 1 -57v-57q0 -3 -4.5 -7t-7.5 -4q-14 -2 -27.5 -3.5t-27.5 -1.5q-49 -1 -95 -1t-95 -1q-17 0 -26.5 -1t-13.5 -5.5t-4 -14
|
||||
t1 -27.5q0 -10 -0.5 -13t0.5 -12q1 -12 2 -19t4 -10.5t10 -4.5t20 -2q10 -2 27.5 -1t28.5 0q12 -1 17.5 -3.5t7.5 -7.5t1.5 -13t-0.5 -20q0 -4 -0.5 -7.5t0.5 -7.5q0 -18 -18 -18q-30 -2 -59 0q-16 0 -25 -0.5t-12.5 -4.5t-4 -12.5t-0.5 -23.5v-74q1 -21 21 -21h243
|
||||
q18 2 25 -2.5t8 -22.5q2 -28 2.5 -62t-0.5 -62q0 -4 -4.5 -8.5t-8.5 -5.5q-6 -1 -27.5 -1h-52t-66.5 0.5l-71 1t-66 0.5h-50q-23 -1 -47 0l-47 2q-19 1 -24.5 6.5t-5.5 24.5v66t1 66q0 23 0.5 44.5t1.5 44.5q-1 0 -0.5 19.5t1 47t1 58t0.5 52.5z" />
|
||||
<glyph glyph-name="F" unicode="F" horiz-adv-x="635"
|
||||
d="M440 638q20 -1 40.5 -1.5t41.5 -0.5q11 0 13.5 -5t2.5 -14q1 -28 1 -56t1 -55q0 -19 -4 -24.5t-24 -5.5h-72t-72 1q-26 0 -51 0.5t-51 0.5q-10 0 -19.5 0.5t-19.5 -0.5q-21 -2 -25.5 -7t-5.5 -25q-1 -19 -1.5 -34t-1.5 -34q0 -23 4 -28.5t28 -7.5q29 -3 58.5 -1.5
|
||||
t58.5 -0.5q11 -1 23 -0.5t23 -0.5q18 -2 26.5 -2t11.5 -3.5t3 -11.5t2 -24q2 -20 2 -41t-1 -41q0 -13 -4 -16.5t-17 -3.5q-45 -1 -90 -1t-90 -1q-4 0 -7.5 0.5t-7.5 0.5q-18 0 -21.5 -3t-3.5 -20q-1 -27 -1 -61t-1 -61q0 -20 -1.5 -30.5t-6.5 -15t-15 -5t-29 -0.5
|
||||
q-25 1 -49 1.5t-49 1.5q-20 2 -25 6.5t-6 22.5q-1 50 -1 99.5t-1 99.5q0 19 -1 64.5t-1 97.5t0.5 99.5t2.5 70.5q2 15 1.5 25.5t1.5 26.5q1 18 5 21.5t23 4.5h16q16 -1 46.5 -1h68h78.5h78.5t68 -0.5t46.5 -0.5z" />
|
||||
<glyph glyph-name="f" unicode="f" horiz-adv-x="635"
|
||||
d="M440 638q20 -1 40.5 -1.5t41.5 -0.5q11 0 13.5 -5t2.5 -14q1 -28 1 -56t1 -55q0 -19 -4 -24.5t-24 -5.5h-72t-72 1q-26 0 -51 0.5t-51 0.5q-10 0 -19.5 0.5t-19.5 -0.5q-21 -2 -25.5 -7t-5.5 -25q-1 -19 -1.5 -34t-1.5 -34q0 -23 4 -28.5t28 -7.5q29 -3 58.5 -1.5
|
||||
t58.5 -0.5q11 -1 23 -0.5t23 -0.5q18 -2 26.5 -2t11.5 -3.5t3 -11.5t2 -24q2 -20 2 -41t-1 -41q0 -13 -4 -16.5t-17 -3.5q-45 -1 -90 -1t-90 -1q-4 0 -7.5 0.5t-7.5 0.5q-18 0 -21.5 -3t-3.5 -20q-1 -27 -1 -61t-1 -61q0 -20 -1.5 -30.5t-6.5 -15t-15 -5t-29 -0.5
|
||||
q-25 1 -49 1.5t-49 1.5q-20 2 -25 6.5t-6 22.5q-1 50 -1 99.5t-1 99.5q0 19 -1 64.5t-1 97.5t0.5 99.5t2.5 70.5q2 15 1.5 25.5t1.5 26.5q1 18 5 21.5t23 4.5h16q16 -1 46.5 -1h68h78.5h78.5t68 -0.5t46.5 -0.5z" />
|
||||
<glyph glyph-name="G" unicode="G" horiz-adv-x="718"
|
||||
d="M475 362h58t58 -1q17 -1 20.5 -4t6.5 -19q5 -32 -0.5 -62.5t-15.5 -59.5q-12 -37 -33 -68t-47 -59q-32 -35 -71 -58t-87 -31q-50 -8 -98.5 -6t-96.5 24q-26 11 -49 22t-45 29q-13 11 -22 24t-20 27q-7 14 -15.5 29.5t-13.5 31.5q-31 89 -18 171.5t60 160.5q27 45 69 73
|
||||
t93 43q55 17 109.5 16.5t109.5 -11.5q26 -5 48 -17t45 -23q8 -4 8 -10t-6 -11l-50.5 -45.5l-50.5 -45.5q-4 -3 -10.5 -5t-8.5 0q-20 14 -41.5 12l-42.5 -4q-17 -2 -33.5 -6t-31.5 -11q-29 -14 -51.5 -41.5t-32.5 -62t-5.5 -72.5t29.5 -74q23 -33 56.5 -47t74.5 -9q19 2 37 7
|
||||
t33 18q7 5 13 11t11 13q5 8 10 18.5t3 18.5q-1 5 -4.5 7t-8.5 2t-10.5 -0.5t-9.5 0.5q-7 1 -13 1t-13 1q-22 2 -28 8.5t-7 28.5q-1 13 -1 25t-2 24q-1 20 16 20q23 0 47 -0.5t47 -0.5q8 -1 15.5 -1.5t15.5 -0.5z" />
|
||||
<glyph glyph-name="g" unicode="g" horiz-adv-x="719"
|
||||
d="M474 362h58t58 -1q17 -1 20.5 -4t6.5 -19q5 -32 -0.5 -62.5t-15.5 -59.5q-12 -37 -33 -68t-47 -59q-32 -35 -71 -58t-87 -31q-50 -8 -98.5 -6t-96.5 24q-26 11 -49 22t-45 29q-13 11 -22 24t-20 27q-7 14 -15.5 29.5t-13.5 31.5q-31 89 -18 171.5t60 160.5q27 45 69 73
|
||||
t93 43q55 17 109.5 16.5t109.5 -11.5q26 -5 48 -17t45 -23q8 -4 8 -10t-6 -11l-50.5 -45.5l-50.5 -45.5q-4 -3 -10.5 -5t-8.5 0q-20 14 -41.5 12l-42.5 -4q-17 -2 -33.5 -6t-31.5 -11q-29 -14 -51.5 -41.5t-32.5 -62t-5.5 -72.5t29.5 -74q23 -33 56.5 -47t74.5 -9q19 2 37 7
|
||||
t33 18q7 5 13 11t11 13q5 8 10 18.5t3 18.5q-1 5 -4.5 7t-8.5 2t-10.5 -0.5t-9.5 0.5q-7 1 -13 1t-13 1q-22 2 -28 8.5t-7 28.5q-1 13 -1 25t-2 24q-1 20 16 20q23 0 47 -0.5t47 -0.5q8 -1 15.5 -1.5t15.5 -0.5z" />
|
||||
<glyph glyph-name="H" unicode="H" horiz-adv-x="645"
|
||||
d="M9 518q0 22 -0.5 49t-0.5 49q0 12 4 16.5t15 4.5h63t62 -1q30 -1 33 -32q1 -11 0.5 -25.5t-0.5 -25.5q-1 -30 -1.5 -60.5t-0.5 -60.5q0 -8 0.5 -16.5t2.5 -16.5q2 -5 7 -9t8 -4h15h106q18 0 28 0.5t15 5t6.5 14t1.5 27.5q2 41 1.5 84t-0.5 84q0 6 -0.5 11.5t1.5 10.5
|
||||
q1 15 18 15q31 -2 61.5 -1.5t62.5 -1.5q14 -1 16 -4t2 -19q0 -26 0.5 -56.5t0.5 -56.5q-1 -24 -1.5 -47.5t-0.5 -47.5q-1 -28 -0.5 -55.5t0.5 -55.5q1 -60 1 -118v-118q0 -7 0.5 -13t-0.5 -13q-2 -17 -9 -23.5t-24 -6.5q-29 -1 -57 -0.5t-57 -0.5q-16 0 -16 16
|
||||
q0 13 0.5 25.5t-0.5 25.5q-1 35 -1.5 68.5t-2.5 68.5q-1 10 -2 16t-4 8.5t-9 3.5t-16 1l-63 2t-62 -1q-10 -2 -15.5 -2.5t-8.5 -3.5t-4 -8.5t-1 -16.5q-1 -39 -1 -80.5v-80.5q0 -21 -7.5 -30t-29.5 -12q-1 -1 -3 -1q-29 0 -57 0.5t-57 0.5q-11 0 -15.5 5.5t-5.5 15.5
|
||||
q-1 13 -1.5 25.5t-0.5 25.5v123.5t1 123.5l0.5 22t1 53l1 65t0.5 59z" />
|
||||
<glyph glyph-name="h" unicode="h" horiz-adv-x="646"
|
||||
d="M9 518q0 22 -0.5 49t-0.5 49q0 12 4 16.5t15 4.5h63t62 -1q30 -1 33 -32q1 -11 0.5 -25.5t-0.5 -25.5q-1 -30 -1.5 -60.5t-0.5 -60.5q0 -8 0.5 -16.5t2.5 -16.5q2 -5 7 -9t8 -4h15h106q18 0 28 0.5t15 5t6.5 14t1.5 27.5q2 41 1.5 84t-0.5 84q0 6 -0.5 11.5t1.5 10.5
|
||||
q1 15 18 15q31 -2 61.5 -1.5t62.5 -1.5q14 -1 16 -4t2 -19q0 -26 0.5 -56.5t0.5 -56.5q-1 -24 -1.5 -47.5t-0.5 -47.5q-1 -28 -0.5 -55.5t0.5 -55.5q1 -60 1 -118v-118q0 -7 0.5 -13t-0.5 -13q-2 -17 -9 -23.5t-24 -6.5q-29 -1 -57 -0.5t-57 -0.5q-16 0 -16 16
|
||||
q0 13 0.5 25.5t-0.5 25.5q-1 35 -1.5 68.5t-2.5 68.5q-1 10 -2 16t-4 8.5t-9 3.5t-16 1l-63 2t-62 -1q-10 -2 -15.5 -2.5t-8.5 -3.5t-4 -8.5t-1 -16.5q-1 -39 -1 -80.5v-80.5q0 -21 -7.5 -30t-29.5 -12q-1 -1 -3 -1q-29 0 -57 0.5t-57 0.5q-11 0 -15.5 5.5t-5.5 15.5
|
||||
q-1 13 -1.5 25.5t-0.5 25.5v123.5t1 123.5l0.5 22t1 53l1 65t0.5 59z" />
|
||||
<glyph glyph-name="I" unicode="I" horiz-adv-x="302"
|
||||
d="M198 248v-226q0 -16 -4.5 -19.5t-21.5 -1.5q-33 4 -66 1.5t-69 -1.5q-31 -5 -31 26q-1 18 -0.5 34t0.5 35q1 45 1 90v90q0 68 -0.5 144t-1.5 144q0 12 0.5 24t1.5 24q2 14 5.5 17.5t16.5 5.5q38 4 74 3.5t73 -5.5q11 -1 18 -2t8 -12q2 -10 1.5 -25t-0.5 -25
|
||||
q-1 -66 -2 -148.5t-3 -172.5z" />
|
||||
<glyph glyph-name="i" unicode="i" horiz-adv-x="302"
|
||||
d="M198 248v-226q0 -16 -4.5 -19.5t-21.5 -1.5q-33 4 -66 1.5t-69 -1.5q-31 -5 -31 26q-1 18 -0.5 34t0.5 35q1 45 1 90v90q0 68 -0.5 144t-1.5 144q0 12 0.5 24t1.5 24q2 14 5.5 17.5t16.5 5.5q38 4 74 3.5t73 -5.5q11 -1 18 -2t8 -12q2 -10 1.5 -25t-0.5 -25
|
||||
q-1 -66 -2 -148.5t-3 -172.5z" />
|
||||
<glyph glyph-name="J" unicode="J" horiz-adv-x="636"
|
||||
d="M524 240q-8 -52 -27.5 -99t-53.5 -88q-19 -23 -42.5 -37.5t-53.5 -20.5q-20 -4 -38.5 -6.5t-38.5 -1.5q-6 0 -30.5 1.5t-56.5 10t-66.5 27.5t-60.5 55q-13 21 -22 44.5t-15 47.5q-7 29 -10.5 59.5t-7.5 60.5q-1 10 0 16.5t4 10.5t9 6.5t16 4.5h15.5h35.5t43 -0.5t38 -0.5
|
||||
q17 0 17 -19q-1 -13 -1 -26t2 -26q5 -39 29 -57t64 -12q36 6 53.5 21.5t25.5 52.5q9 48 9 96q0 59 -1.5 112t-2.5 113q-1 17 0 27t5.5 15t13.5 7t26 2q45 2 92 0q25 -1 31.5 -8.5t8.5 -34.5v-34.5v-58t-1 -72l-2 -74.5t-2.5 -66.5t-4.5 -47.5z" />
|
||||
<glyph glyph-name="j" unicode="j" horiz-adv-x="635"
|
||||
d="M524 240q-8 -52 -27.5 -99t-53.5 -88q-19 -23 -42.5 -37.5t-53.5 -20.5q-20 -4 -38.5 -6.5t-38.5 -1.5q-6 0 -30.5 1.5t-56.5 10t-66.5 27.5t-60.5 55q-13 21 -22 44.5t-15 47.5q-7 29 -10.5 59.5t-7.5 60.5q-1 10 0 16.5t4 10.5t9 6.5t16 4.5h15.5h35.5t43 -0.5t38 -0.5
|
||||
q17 0 17 -19q-1 -13 -1 -26t2 -26q5 -39 29 -57t64 -12q36 6 53.5 21.5t25.5 52.5q9 48 9 96q0 59 -1.5 112t-2.5 113q-1 17 0 27t5.5 15t13.5 7t26 2q45 2 92 0q25 -1 31.5 -8.5t8.5 -34.5v-34.5v-58t-1 -72l-2 -74.5t-2.5 -66.5t-4.5 -47.5z" />
|
||||
<glyph glyph-name="K" unicode="K" horiz-adv-x="656"
|
||||
d="M3 609q0 20 5.5 24.5t25.5 5.5h44t44 -1q26 -1 35 -12.5t8 -38.5q-2 -37 -0.5 -74t1.5 -74q0 -7 4 -9t10 2l63.5 55.5l63.5 56.5q18 16 38 39t38 40q11 11 23 14t27 2q55 -3 111 -3q8 0 16.5 -1t11.5 -10q6 -19 -11 -33q-20 -17 -41 -41t-41 -42l-62.5 -56.5l-62.5 -56.5
|
||||
q-8 -7 -12 -11t-4.5 -7.5t1.5 -8.5t7 -13l58 -99l58 -98l39.5 -62.5l39.5 -61.5q4 -6 7 -11t-2 -12q-7 -13 -25 -13q-9 1 -28 0.5t-39.5 -0.5t-39 0.5t-26.5 3.5t-14 10.5t-11 17t-9.5 19t-8.5 16.5l-54 90.5l-52 91.5l-12 18q-8 13 -18 4q-9 -8 -17.5 -16.5t-17.5 -16.5
|
||||
q-8 -7 -9 -16t-1 -18q0 -39 0.5 -77.5t0.5 -77.5v-26q-2 -20 -21 -20q-29 2 -57 0t-57 0q-16 2 -21.5 9t-5.5 24q-1 38 -1 79v79q0 33 0.5 66t0.5 66q-1 0 -1 31v75.5t0.5 93.5t0.5 84z" />
|
||||
<glyph glyph-name="k" unicode="k" horiz-adv-x="656"
|
||||
d="M3 609q0 20 5.5 24.5t25.5 5.5h44t44 -1q26 -1 35 -12.5t8 -38.5q-2 -37 -0.5 -74t1.5 -74q0 -7 4 -9t10 2l63.5 55.5l63.5 56.5q18 16 38 39t38 40q11 11 23 14t27 2q55 -3 111 -3q8 0 16.5 -1t11.5 -10q6 -19 -11 -33q-20 -17 -41 -41t-41 -42l-62.5 -56.5l-62.5 -56.5
|
||||
q-8 -7 -12 -11t-4.5 -7.5t1.5 -8.5t7 -13l58 -99l58 -98l39.5 -62.5l39.5 -61.5q4 -6 7 -11t-2 -12q-7 -13 -25 -13q-9 1 -28 0.5t-39.5 -0.5t-39 0.5t-26.5 3.5t-14 10.5t-11 17t-9.5 19t-8.5 16.5l-54 90.5l-52 91.5l-12 18q-8 13 -18 4q-9 -8 -17.5 -16.5t-17.5 -16.5
|
||||
q-8 -7 -9 -16t-1 -18q0 -39 0.5 -77.5t0.5 -77.5v-26q-2 -20 -21 -20q-29 2 -57 0t-57 0q-16 2 -21.5 9t-5.5 24q-1 38 -1 79v79q0 33 0.5 66t0.5 66q-1 0 -1 31v75.5t0.5 93.5t0.5 84z" />
|
||||
<glyph glyph-name="L" unicode="L" horiz-adv-x="521"
|
||||
d="M3 616q0 18 16 19q29 2 54.5 1.5t54.5 0.5q23 1 30 -6t8 -30q3 -71 3.5 -147.5t3.5 -147.5q1 -30 -0.5 -57t0.5 -56q1 -19 5 -23t24 -4q35 -2 70 -2h70h47t20.5 -6t6 -20t2.5 -42q2 -20 2.5 -38.5t2.5 -38.5q1 -20 -21 -20q-23 1 -47 1.5t-48 0.5h-124t-123 1h-11.5
|
||||
t-10.5 1q-20 2 -27 9.5t-7 28.5q0 42 0.5 84.5t0.5 84.5v21v55t0.5 77.5t0 89l-1 88t-1.5 75.5z" />
|
||||
<glyph glyph-name="l" unicode="l" horiz-adv-x="521"
|
||||
d="M3 616q0 18 16 19q29 2 54.5 1.5t54.5 0.5q23 1 30 -6t8 -30q3 -71 3.5 -147.5t3.5 -147.5q1 -30 -0.5 -57t0.5 -56q1 -19 5 -23t24 -4q35 -2 70 -2h70h47t20.5 -6t6 -20t2.5 -42q2 -20 2.5 -38.5t2.5 -38.5q1 -20 -21 -20q-23 1 -47 1.5t-48 0.5h-124t-123 1h-11.5
|
||||
t-10.5 1q-20 2 -27 9.5t-7 28.5q0 42 0.5 84.5t0.5 84.5v21v55t0.5 77.5t0 89l-1 88t-1.5 75.5z" />
|
||||
<glyph glyph-name="M" unicode="M" horiz-adv-x="980"
|
||||
d="M181 299q0 -7 -0.5 -8.5t0.5 -5.5l6 -131l6 -131q1 -14 -2 -17t-19 -5q-17 -2 -33 -2h-32h-45t-44 1q-15 0 -12 15q-1 57 -0.5 113.5t0.5 113.5q-3 184 -3 368q-1 17 3 21t20 5h26.5h39.5h40.5h28.5q16 2 25.5 -4t17.5 -20l59.5 -110.5l60.5 -109.5l72 -132
|
||||
q8 -14 13 -13.5t13 14.5l27 45l27 45q20 34 41.5 66.5t41.5 67.5l34.5 61.5l32.5 62.5q8 14 17.5 22.5t26.5 6.5q6 -1 25.5 -1h41t39.5 0.5t20 0.5q17 0 26 -1t13.5 -5.5t5 -14t0.5 -27.5q0 -99 1 -197t3 -197q1 -44 0.5 -84.5t1.5 -83.5q1 -23 -2.5 -27t-25.5 -3
|
||||
q-9 0 -27.5 0.5t-38.5 0.5h-38h-25q-14 -1 -18.5 3t-4.5 19q-1 69 -1.5 136t-1.5 136q0 8 -1 11q0 5 -4 9q-2 2 -3.5 0t-2.5 -3l-9 -13t-8 -13l-76.5 -131.5l-76.5 -132.5q-5 -8 -11 -14t-20 -6h-87q-22 0 -29 8.5t-15 25.5l-57 118l-59 117q-4 8 -8.5 16t-9.5 16
|
||||
q-1 2 -3.5 3.5t-2.5 -5.5z" />
|
||||
<glyph glyph-name="m" unicode="m" horiz-adv-x="979"
|
||||
d="M181 299q0 -7 -0.5 -8.5t0.5 -5.5l6 -131l6 -131q1 -14 -2 -17t-19 -5q-17 -2 -33 -2h-32h-45t-44 1q-15 0 -12 15q-1 57 -0.5 113.5t0.5 113.5q-3 184 -3 368q-1 17 3 21t20 5h26.5h39.5h40.5h28.5q16 2 25.5 -4t17.5 -20l59.5 -110.5l60.5 -109.5l72 -132
|
||||
q8 -14 13 -13.5t13 14.5l27 45l27 45q20 34 41.5 66.5t41.5 67.5l34.5 61.5l32.5 62.5q8 14 17.5 22.5t26.5 6.5q6 -1 25.5 -1h41t39.5 0.5t20 0.5q17 0 26 -1t13.5 -5.5t5 -14t0.5 -27.5q0 -99 1 -197t3 -197q1 -44 0.5 -84.5t1.5 -83.5q1 -23 -2.5 -27t-25.5 -3
|
||||
q-9 0 -27.5 0.5t-38.5 0.5h-38h-25q-14 -1 -18.5 3t-4.5 19q-1 69 -1.5 136t-1.5 136q0 8 -1 11q0 5 -4 9q-2 2 -3.5 0t-2.5 -3l-9 -13t-8 -13l-76.5 -131.5l-76.5 -132.5q-5 -8 -11 -14t-20 -6h-87q-22 0 -29 8.5t-15 25.5l-57 118l-59 117q-4 8 -8.5 16t-9.5 16
|
||||
q-1 2 -3.5 3.5t-2.5 -5.5z" />
|
||||
<glyph glyph-name="n" unicode="n" horiz-adv-x="750"
|
||||
d="M4 601v22q2 17 17 14q38 1 71.5 0t68.5 1q13 1 19.5 -3.5t13.5 -13.5q27 -39 54 -83l55.5 -87.5l58 -86t60.5 -78.5q7 -8 17 -18.5t14 -10.5t5 15t1 26q-1 33 -1.5 72t-1.5 78.5t-1.5 78.5t-1.5 72q-1 22 3.5 28t25.5 7q29 2 57 3t57 0q26 0 32 -6.5t6 -31.5
|
||||
q0 -21 -0.5 -48l-1 -55l-1 -55t-0.5 -48q2 -55 3.5 -110t3.5 -111q2 -38 2 -75.5t1 -75.5q0 -13 -3 -16.5t-16 -3.5h-84.5t-85.5 2q-6 0 -13 3.5t-12 8.5q-29 27 -50 54q-11 13 -27 35t-33.5 48.5l-36 55l-34.5 52.5l-27.5 41.5l-15.5 23.5q-1 2 -4 2t-5 1q-1 -1 -1 -2
|
||||
q-1 -1 -1 -5v-46.5t1 -46.5l2 -96t3 -96q0 -23 -7 -30t-30 -6l-31.5 2t-31.5 1q-16 -1 -37.5 0t-38.5 -2q-13 -3 -16 10q-2 8 -1 13t1 13q-3 68 -2 135t-1 134q-1 36 0 72t1 72l-0.5 17.5t-0.5 41.5t0.5 49.5t0.5 42.5z" />
|
||||
<glyph glyph-name="N" unicode="N" horiz-adv-x="750"
|
||||
d="M4 601v22q2 17 17 14q38 1 71.5 0t68.5 1q13 1 19.5 -3.5t13.5 -13.5q27 -39 54 -83l55.5 -87.5l58 -86t60.5 -78.5q7 -8 17 -18.5t14 -10.5t5 15t1 26q-1 33 -1.5 72t-1.5 78.5t-1.5 78.5t-1.5 72q-1 22 3.5 28t25.5 7q29 2 57 3t57 0q26 0 32 -6.5t6 -31.5
|
||||
q0 -21 -0.5 -48l-1 -55l-1 -55t-0.5 -48q2 -55 3.5 -110t3.5 -111q2 -38 2 -75.5t1 -75.5q0 -13 -3 -16.5t-16 -3.5h-84.5t-85.5 2q-6 0 -13 3.5t-12 8.5q-29 27 -50 54q-11 13 -27 35t-33.5 48.5l-36 55l-34.5 52.5l-27.5 41.5l-15.5 23.5q-1 2 -4 2t-5 1q-1 -1 -1 -2
|
||||
q-1 -1 -1 -5v-46.5t1 -46.5l2 -96t3 -96q0 -23 -7 -30t-30 -6l-31.5 2t-31.5 1q-16 -1 -37.5 0t-38.5 -2q-13 -3 -16 10q-2 8 -1 13t1 13q-3 68 -2 135t-1 134q-1 36 0 72t1 72l-0.5 17.5t-0.5 41.5t0.5 49.5t0.5 42.5z" />
|
||||
<glyph glyph-name="O" unicode="O" horiz-adv-x="728"
|
||||
d="M632 326q-5 -46 -15 -89t-29.5 -81.5t-49 -71t-71.5 -56.5q-33 -19 -68 -29t-73 -10q-62 0 -110.5 15.5t-85 40t-62 55t-42 60t-24.5 54.5t-9 40q-2 27 -4.5 54t2.5 55q10 49 33.5 96t60 85t86.5 64.5t112 33.5q37 5 73.5 -1t71.5 -19q60 -23 101 -64.5t67 -100.5
|
||||
q14 -32 26 -63t10 -68zM466 330q0 12 -0.5 18.5t-1.5 10.5t-2 7.5t-2 9.5q-3 15 -16.5 32.5t-33 33t-43 26t-45.5 10.5q-45 0 -75.5 -18t-49 -44.5t-27.5 -57.5t-10 -57q-1 -17 8.5 -39.5t29 -43t48 -35t64.5 -14.5q40 0 69 16.5t48.5 40.5t29 52t9.5 52z" />
|
||||
<glyph glyph-name="o" unicode="o"
|
||||
d="M634 326q-5 -46 -15 -89t-29.5 -81.5t-49 -71t-71.5 -56.5q-33 -19 -68 -29t-73 -10q-62 0 -110.5 15.5t-85 40t-62 55t-42 60t-24.5 54.5t-9 40q-2 27 -4.5 54t2.5 55q10 49 33.5 96t60 85t86.5 64.5t112 33.5q37 5 73.5 -1t71.5 -19q60 -23 101 -64.5t67 -100.5
|
||||
q14 -32 26 -63t10 -68zM468 330q0 12 -0.5 18.5t-1.5 10.5t-2 7.5t-2 9.5q-3 15 -16.5 32.5t-33 33t-43 26t-45.5 10.5q-45 0 -75.5 -18t-49 -44.5t-27.5 -57.5t-10 -57q-1 -17 8.5 -39.5t29 -43t48 -35t64.5 -14.5q40 0 69 16.5t48.5 40.5t29 52t9.5 52z" />
|
||||
<glyph glyph-name="p" unicode="p" horiz-adv-x="635"
|
||||
d="M11 527q1 17 0 34.5t-1 35.5q1 21 8 29t28 10q17 2 33 2h32q54 -1 108 -1t108 -2q56 -3 105 -21q14 -5 35.5 -21.5t41 -42t33.5 -58t14 -68.5q0 -56 -19 -95t-49 -66.5t-66.5 -45t-71.5 -28.5q-59 -18 -122 -12q-21 2 -25 -4t-4 -29q-1 -30 -0.5 -55.5t2.5 -54.5
|
||||
q1 -21 -6 -30t-28 -8q-31 0 -65 -1.5t-65 0.5q-14 1 -18.5 6.5t-6.5 20.5q-2 27 -1 54t0 54q-2 51 -2.5 100.5t-2.5 100.5l0.5 21.5t1 52t1.5 64.5t2 58zM225 520q-20 -2 -24 -6t-3 -24q0 -27 0.5 -64t1.5 -64q1 -14 5 -19t17 -4q10 1 34.5 2t50 9t44.5 25t19 50
|
||||
q0 26 -10.5 43.5t-26 28.5t-34 16t-34.5 6.5t-27 1t-13 -0.5z" />
|
||||
<glyph glyph-name="P" unicode="P" horiz-adv-x="635"
|
||||
d="M11 527q1 17 0 34.5t-1 35.5q1 21 8 29t28 10q17 2 33 2h32q54 -1 108 -1t108 -2q56 -3 105 -21q14 -5 35.5 -21.5t41 -42t33.5 -58t14 -68.5q0 -56 -19 -95t-49 -66.5t-66.5 -45t-71.5 -28.5q-59 -18 -122 -12q-21 2 -25 -4t-4 -29q-1 -30 -0.5 -55.5t2.5 -54.5
|
||||
q1 -21 -6 -30t-28 -8q-31 0 -65 -1.5t-65 0.5q-14 1 -18.5 6.5t-6.5 20.5q-2 27 -1 54t0 54q-2 51 -2.5 100.5t-2.5 100.5l0.5 21.5t1 52t1.5 64.5t2 58zM225 520q-20 -2 -24 -6t-3 -24q0 -27 0.5 -64t1.5 -64q1 -14 5 -19t17 -4q10 1 34.5 2t50 9t44.5 25t19 50
|
||||
q0 26 -10.5 43.5t-26 28.5t-34 16t-34.5 6.5t-27 1t-13 -0.5z" />
|
||||
<glyph glyph-name="q" unicode="q" horiz-adv-x="792"
|
||||
d="M679 27q13 -13 0 -21l-55 -38.5l-55 -37.5q-12 -8 -23 5l-27.5 33.5l-27.5 34.5q-12 17 -32 10q-16 -6 -32 -11t-33 -8q-42 -8 -82 -0.5t-79 21.5q-16 5 -31.5 12t-30.5 15q-47 23 -83.5 56.5t-55.5 83.5q-7 18 -12 39.5t-8.5 42.5t-5 40t-1.5 32q0 22 6.5 48t18.5 52.5
|
||||
t29 52t38 46.5q50 50 110 76.5t127 34.5q45 6 88.5 -5t82.5 -36q66 -40 102 -97.5t51 -132.5q11 -58 -0.5 -110t-37.5 -103l-12 -26t-3.5 -16.5t7 -16t19.5 -22.5l24 -27.5zM471 256q14 42 11.5 84t-26.5 76q-22 30 -49 43.5t-51.5 17.5t-42 2.5t-20.5 -1.5
|
||||
q-20 -5 -38.5 -17.5t-29.5 -30.5q-16 -25 -28 -55t-12 -56t5.5 -39.5l12.5 -30.5q20 -46 62 -64.5t93 -15.5q43 2 71 24.5t42 62.5z" />
|
||||
<glyph glyph-name="Q" unicode="Q" horiz-adv-x="792"
|
||||
d="M679 27q13 -13 0 -21l-55 -38.5l-55 -37.5q-12 -8 -23 5l-27.5 33.5l-27.5 34.5q-12 17 -32 10q-16 -6 -32 -11t-33 -8q-42 -8 -82 -0.5t-79 21.5q-16 5 -31.5 12t-30.5 15q-47 23 -83.5 56.5t-55.5 83.5q-7 18 -12 39.5t-8.5 42.5t-5 40t-1.5 32q0 22 6.5 48t18.5 52.5
|
||||
t29 52t38 46.5q50 50 110 76.5t127 34.5q45 6 88.5 -5t82.5 -36q66 -40 102 -97.5t51 -132.5q11 -58 -0.5 -110t-37.5 -103l-12 -26t-3.5 -16.5t7 -16t19.5 -22.5l24 -27.5zM471 256q14 42 11.5 84t-26.5 76q-22 30 -49 43.5t-51.5 17.5t-42 2.5t-20.5 -1.5
|
||||
q-20 -5 -38.5 -17.5t-29.5 -30.5q-16 -25 -28 -55t-12 -56t5.5 -39.5l12.5 -30.5q20 -46 62 -64.5t93 -15.5q43 2 71 24.5t42 62.5z" />
|
||||
<glyph glyph-name="R" unicode="R" horiz-adv-x="656"
|
||||
d="M235 638q54 -2 107.5 -6t105.5 -25q26 -11 47 -26.5t36 -38.5q23 -34 32 -71.5t6 -74t-16 -70t-34 -60.5q-10 -12 -24.5 -21.5l-28.5 -18.5q-8 -5 -9.5 -10t3.5 -13l34.5 -57.5l35.5 -57.5q7 -11 17 -30t18 -30q7 -12 2 -24q-3 -8 -8.5 -9t-12.5 -1q-32 2 -63 1t-63 -1
|
||||
q-11 0 -16 4t-10 12l-41.5 78.5t-42.5 78.5q-4 7 -8 13l-8 12q-8 15 -27 15q-11 0 -20.5 1.5t-20.5 1.5q-16 1 -21.5 -3.5t-5.5 -21.5l2 -78l2 -78q1 -14 -3 -20t-17 -9q-5 -2 -11 -2h-11q-26 0 -50.5 -0.5t-50.5 -0.5q-8 0 -15 0.5t-15 1.5q-17 2 -20 19q-1 8 -2.5 16.5
|
||||
t-1.5 16.5q1 40 1 80t2 80q1 38 1 74.5v74.5q1 61 0 121.5t-1 121.5q0 22 6 27.5t29 4.5h84l11.5 0.5t28.5 1.5t35 1.5t32 -0.5zM201 413q-1 -14 -1 -27v-41q0 -17 18 -17q20 2 39.5 2.5t39.5 2.5q20 3 38.5 11t33.5 24q11 12 14.5 29t0 33.5t-13.5 29t-25 16.5
|
||||
q-68 17 -133 14q-3 -1 -6.5 -5.5t-3.5 -7.5q-1 -14 -1 -32v-32z" />
|
||||
<glyph glyph-name="r" unicode="r" horiz-adv-x="656"
|
||||
d="M235 638q54 -2 107.5 -6t105.5 -25q26 -11 47 -26.5t36 -38.5q23 -34 32 -71.5t6 -74t-16 -70t-34 -60.5q-10 -12 -24.5 -21.5l-28.5 -18.5q-8 -5 -9.5 -10t3.5 -13l34.5 -57.5l35.5 -57.5q7 -11 17 -30t18 -30q7 -12 2 -24q-3 -8 -8.5 -9t-12.5 -1q-32 2 -63 1t-63 -1
|
||||
q-11 0 -16 4t-10 12l-41.5 78.5t-42.5 78.5q-4 7 -8 13l-8 12q-8 15 -27 15q-11 0 -20.5 1.5t-20.5 1.5q-16 1 -21.5 -3.5t-5.5 -21.5l2 -78l2 -78q1 -14 -3 -20t-17 -9q-5 -2 -11 -2h-11q-26 0 -50.5 -0.5t-50.5 -0.5q-8 0 -15 0.5t-15 1.5q-17 2 -20 19q-1 8 -2.5 16.5
|
||||
t-1.5 16.5q1 40 1 80t2 80q1 38 1 74.5v74.5q1 61 0 121.5t-1 121.5q0 22 6 27.5t29 4.5h84l11.5 0.5t28.5 1.5t35 1.5t32 -0.5zM201 413q-1 -14 -1 -27v-41q0 -17 18 -17q20 2 39.5 2.5t39.5 2.5q20 3 38.5 11t33.5 24q11 12 14.5 29t0 33.5t-13.5 29t-25 16.5
|
||||
q-68 17 -133 14q-3 -1 -6.5 -5.5t-3.5 -7.5q-1 -14 -1 -32v-32z" />
|
||||
<glyph glyph-name="s" unicode="s" horiz-adv-x="584"
|
||||
d="M384 631q26 -11 44 -30l37 -38q6 -6 4 -14t-7 -13l-36 -39.5l-37 -38.5q-8 -9 -20 -2l-20 16t-21 14l-28 15.5t-28 12.5q-17 6 -30.5 1.5t-20.5 -18.5q-6 -12 -3.5 -28.5t14.5 -25.5q16 -11 32.5 -20.5l33.5 -18.5l48.5 -27.5t47.5 -27.5t41.5 -33.5t35.5 -40.5
|
||||
q20 -26 24.5 -56t0.5 -61q-14 -120 -131 -161q-46 -16 -90.5 -14.5t-90.5 14.5q-42 11 -77 31.5l-70 42.5q-6 3 -12 9q-9 8 -13.5 12.5t-5.5 9.5t1.5 11.5t8.5 17.5q11 21 20.5 43l19.5 44q4 8 7 15t8 14q3 5 8 6.5t10 -3.5l22 -22l22 -22q26 -25 57.5 -39.5t64.5 -25.5
|
||||
q13 -4 30 -1q15 3 25 12t13.5 21t0.5 25t-14 24q-13 13 -28 23.5t-31 19.5q-26 16 -53 30.5t-55 27.5q-42 20 -65.5 52.5t-35.5 75.5q-15 62 21 112q14 20 28 33.5t44 28.5q41 20 80 25.5t71.5 3t58 -9.5t39.5 -13z" />
|
||||
<glyph glyph-name="S" unicode="S" horiz-adv-x="584"
|
||||
d="M384 631q26 -11 44 -30l37 -38q6 -6 4 -14t-7 -13l-36 -39.5l-37 -38.5q-8 -9 -20 -2l-20 16t-21 14l-28 15.5t-28 12.5q-17 6 -30.5 1.5t-20.5 -18.5q-6 -12 -3.5 -28.5t14.5 -25.5q16 -11 32.5 -20.5l33.5 -18.5l48.5 -27.5t47.5 -27.5t41.5 -33.5t35.5 -40.5
|
||||
q20 -26 24.5 -56t0.5 -61q-14 -120 -131 -161q-46 -16 -90.5 -14.5t-90.5 14.5q-42 11 -77 31.5l-70 42.5q-6 3 -12 9q-9 8 -13.5 12.5t-5.5 9.5t1.5 11.5t8.5 17.5q11 21 20.5 43l19.5 44q4 8 7 15t8 14q3 5 8 6.5t10 -3.5l22 -22l22 -22q26 -25 57.5 -39.5t64.5 -25.5
|
||||
q13 -4 30 -1q15 3 25 12t13.5 21t0.5 25t-14 24q-13 13 -28 23.5t-31 19.5q-26 16 -53 30.5t-55 27.5q-42 20 -65.5 52.5t-35.5 75.5q-15 62 21 112q14 20 28 33.5t44 28.5q41 20 80 25.5t71.5 3t58 -9.5t39.5 -13z" />
|
||||
<glyph glyph-name="T" unicode="T" horiz-adv-x="646"
|
||||
d="M350 32q0 -20 -5.5 -25.5t-25.5 -6.5q-26 0 -51.5 -0.5t-51.5 -0.5q-16 -1 -20 3t-5 20q-1 63 -1 125t-1 125q-1 35 -1.5 77.5t-2.5 77.5q-1 15 -1 22.5t-3 11t-11 4t-24 0.5h-103q-11 0 -17.5 1t-10 4t-5 9t-2.5 17q-2 30 -2.5 60.5t-0.5 60.5q0 10 6 13.5t14 3.5
|
||||
q120 3 239 3t239 -4q17 -1 27 -9t11 -22q2 -30 2.5 -59.5t1.5 -58.5q0 -18 -19 -16l-39 2t-38 1q-39 1 -78 0q-14 -1 -17 -4.5t-3 -17.5v-50t-0.5 -74t0 -85.5t0 -85t-0.5 -73.5v-49z" />
|
||||
<glyph glyph-name="t" unicode="t" horiz-adv-x="646"
|
||||
d="M350 32q0 -20 -5.5 -25.5t-25.5 -6.5q-26 0 -51.5 -0.5t-51.5 -0.5q-16 -1 -20 3t-5 20q-1 63 -1 125t-1 125q-1 35 -1.5 77.5t-2.5 77.5q-1 15 -1 22.5t-3 11t-11 4t-24 0.5h-103q-11 0 -17.5 1t-10 4t-5 9t-2.5 17q-2 30 -2.5 60.5t-0.5 60.5q0 10 6 13.5t14 3.5
|
||||
q120 3 239 3t239 -4q17 -1 27 -9t11 -22q2 -30 2.5 -59.5t1.5 -58.5q0 -18 -19 -16l-39 2t-38 1q-39 1 -78 0q-14 -1 -17 -4.5t-3 -17.5v-50t-0.5 -74t0 -85.5t0 -85t-0.5 -73.5v-49z" />
|
||||
<glyph glyph-name="U" unicode="U" horiz-adv-x="656"
|
||||
d="M6 611q0 18 4 22t24 4q30 -2 60 -0.5t60 0.5q17 -1 20 -3.5t3 -19.5l-2 -61.5l-2 -61.5q-1 -45 -0.5 -88.5t0.5 -87.5q0 -25 6 -48.5t20 -44.5q37 -61 105 -49q23 3 36 22q43 58 40 126q-2 60 -0.5 120t-0.5 120q0 5 -0.5 14.5t-1 20l-1 20.5t-0.5 15q0 6 17.5 7.5
|
||||
t41.5 1.5t47.5 -1.5t35.5 -2.5q9 -1 14.5 -0.5t9 0t5 -4t2.5 -12.5q2 -26 3.5 -56.5t2.5 -55.5q2 -37 0.5 -73.5t-2.5 -73.5q0 -25 -1.5 -50t-5.5 -50q-11 -73 -43 -132.5t-97 -100.5q-20 -13 -45.5 -20t-50 -10t-45.5 -3t-33 1q-8 1 -27.5 4.5t-44 13.5t-51.5 27t-49 44
|
||||
t-36.5 65.5t-14.5 91.5q-1 47 -1 93.5v93.5l-0.5 19.5t-1 48t-0.5 60v55.5z" />
|
||||
<glyph glyph-name="u" unicode="u" horiz-adv-x="656"
|
||||
d="M6 611q0 18 4 22t24 4q30 -2 60 -0.5t60 0.5q17 -1 20 -3.5t3 -19.5l-2 -61.5l-2 -61.5q-1 -45 -0.5 -88.5t0.5 -87.5q0 -25 6 -48.5t20 -44.5q37 -61 105 -49q23 3 36 22q43 58 40 126q-2 60 -0.5 120t-0.5 120q0 5 -0.5 14.5t-1 20l-1 20.5t-0.5 15q0 6 17.5 7.5
|
||||
t41.5 1.5t47.5 -1.5t35.5 -2.5q9 -1 14.5 -0.5t9 0t5 -4t2.5 -12.5q2 -26 3.5 -56.5t2.5 -55.5q2 -37 0.5 -73.5t-2.5 -73.5q0 -25 -1.5 -50t-5.5 -50q-11 -73 -43 -132.5t-97 -100.5q-20 -13 -45.5 -20t-50 -10t-45.5 -3t-33 1q-8 1 -27.5 4.5t-44 13.5t-51.5 27t-49 44
|
||||
t-36.5 65.5t-14.5 91.5q-1 47 -1 93.5v93.5l-0.5 19.5t-1 48t-0.5 60v55.5z" />
|
||||
<glyph glyph-name="V" unicode="V" horiz-adv-x="719"
|
||||
d="M177 635q9 0 12 -5.5t5 -10.5q5 -10 8 -20l6 -20l37 -137.5l37 -137.5q3 -10 8 -22q2 -2 4.5 -3t4.5 -3q2 0 5 5q4 10 6 14q29 76 61 151.5t55 153.5q11 35 44 35h40h49.5h43h20.5q14 -3 10 -17q0 -4 -1.5 -7.5t-3.5 -7.5l-21 -56t-21 -55l-60.5 -140.5l-59.5 -139.5
|
||||
l-39 -94.5l-39 -95.5q-8 -20 -14 -21.5t-25 -1.5h-167q-9 1 -11 7t-4 13l-15.5 51.5l-14.5 51.5l-38.5 145.5l-38.5 144.5q-12 43 -25 85l-26 84q-2 6 -3.5 11.5t-3.5 11.5q-5 19 1 25t26 5q4 0 24 0.5t44.5 0.5h47.5h32z" />
|
||||
<glyph glyph-name="v" unicode="v" horiz-adv-x="719"
|
||||
d="M177 635q9 0 12 -5.5t5 -10.5q5 -10 8 -20l6 -20l37 -137.5l37 -137.5q3 -10 8 -22q2 -2 4.5 -3t4.5 -3q2 0 5 5q4 10 6 14q29 76 61 151.5t55 153.5q11 35 44 35h40h49.5h43h20.5q14 -3 10 -17q0 -4 -1.5 -7.5t-3.5 -7.5l-21 -56t-21 -55l-60.5 -140.5l-59.5 -139.5
|
||||
l-39 -94.5l-39 -95.5q-8 -20 -14 -21.5t-25 -1.5h-167q-9 1 -11 7t-4 13l-15.5 51.5l-14.5 51.5l-38.5 145.5l-38.5 144.5q-12 43 -25 85l-26 84q-2 6 -3.5 11.5t-3.5 11.5q-5 19 1 25t26 5q4 0 24 0.5t44.5 0.5h47.5h32z" />
|
||||
<glyph glyph-name="W" unicode="W" horiz-adv-x="927"
|
||||
d="M604 308q2 0 3.5 4.5t2.5 11t1.5 13t1.5 10.5q11 63 21 130.5l20 130.5q1 5 2 9t2 9q3 12 16 12q35 -2 70 -1t70 -1q18 -2 21.5 -7.5t0.5 -23.5q-11 -57 -21.5 -122.5t-21.5 -122.5l-23.5 -133l-22.5 -132q-3 -17 -7.5 -34.5t-7.5 -33.5q-4 -16 -7 -19.5t-19 -4.5
|
||||
q-35 -1 -69.5 0.5t-69.5 -0.5q-16 -1 -24.5 6t-13.5 21l-25.5 61.5l-25.5 60.5l-26 59.5l-26 59.5q-2 2 -3.5 5.5t-5.5 3.5q-3 0 -5 -4t-4 -7q-34 -59 -61.5 -121l-53.5 -125q-2 -10 -6 -13q-4 -4 -8 -7t-9 -3q-29 -2 -57 -0.5t-57 -0.5q-14 0 -18 14l-24.5 105.5
|
||||
l-24.5 105.5l-22.5 103l-23.5 102q-4 18 -8 40l-8 45.5t-8.5 45.5l-8.5 40q-1 4 -2 7t-2 7q-2 14 4.5 19t20.5 4q9 -1 29 -1.5t42.5 -0.5t42 0.5t28.5 1.5q8 0 10.5 -3t4.5 -9q4 -14 7.5 -31l7 -35l7 -35.5t6.5 -30.5l19 -73.5t19 -72.5q1 -3 2.5 -6.5t6.5 -3.5q3 0 7 8
|
||||
l42 69t40 70q17 35 55 32q21 -2 42 -0.5t43 0.5q8 0 12 -4t8 -12q19 -45 39 -87.5l40 -86.5q2 -5 5.5 -11.5t7.5 -6.5z" />
|
||||
<glyph glyph-name="w" unicode="w" horiz-adv-x="927"
|
||||
d="M604 308q2 0 3.5 4.5t2.5 11t1.5 13t1.5 10.5q11 63 21 130.5l20 130.5q1 5 2 9t2 9q3 12 16 12q35 -2 70 -1t70 -1q18 -2 21.5 -7.5t0.5 -23.5q-11 -57 -21.5 -122.5t-21.5 -122.5l-23.5 -133l-22.5 -132q-3 -17 -7.5 -34.5t-7.5 -33.5q-4 -16 -7 -19.5t-19 -4.5
|
||||
q-35 -1 -69.5 0.5t-69.5 -0.5q-16 -1 -24.5 6t-13.5 21l-25.5 61.5l-25.5 60.5l-26 59.5l-26 59.5q-2 2 -3.5 5.5t-5.5 3.5q-3 0 -5 -4t-4 -7q-34 -59 -61.5 -121l-53.5 -125q-2 -10 -6 -13q-4 -4 -8 -7t-9 -3q-29 -2 -57 -0.5t-57 -0.5q-14 0 -18 14l-24.5 105.5
|
||||
l-24.5 105.5l-22.5 103l-23.5 102q-4 18 -8 40l-8 45.5t-8.5 45.5l-8.5 40q-1 4 -2 7t-2 7q-2 14 4.5 19t20.5 4q9 -1 29 -1.5t42.5 -0.5t42 0.5t28.5 1.5q8 0 10.5 -3t4.5 -9q4 -14 7.5 -31l7 -35l7 -35.5t6.5 -30.5l19 -73.5t19 -72.5q1 -3 2.5 -6.5t6.5 -3.5q3 0 7 8
|
||||
l42 69t40 70q17 35 55 32q21 -2 42 -0.5t43 0.5q8 0 12 -4t8 -12q19 -45 39 -87.5l40 -86.5q2 -5 5.5 -11.5t7.5 -6.5z" />
|
||||
<glyph glyph-name="X" unicode="X" horiz-adv-x="698"
|
||||
d="M107 639h73q18 0 25 -15q9 -21 21 -39.5t25.5 -36.5l27 -36.5t25.5 -37.5q6 -11 10 -10.5t11 11.5l42 66l41 67q4 8 7.5 13.5t8.5 9t13.5 5t23.5 1.5h75q14 0 32 0.5t32 -0.5q16 -1 14 -10t-10 -22l-53.5 -77l-52.5 -78l-41.5 -60.5l-41.5 -59.5q-8 -11 -7.5 -19.5
|
||||
t6.5 -19.5l21 -34t22 -34l64.5 -88.5l65.5 -87.5q1 -2 5.5 -7t8.5 -11.5t6.5 -13t0.5 -11.5q-2 -3 -12.5 -4t-18.5 -1q-36 0 -71.5 1.5t-70.5 0.5q-14 -1 -22.5 4.5t-15.5 15.5l-75 117q-2 4 -4.5 8t-5.5 8q-9 9 -16 -1l-14 -18t-14 -19l-35.5 -52l-35.5 -52q-7 -10 -19 -12
|
||||
h-72.5t-73.5 1q-7 0 -11 1q-25 6 -10 27l76.5 108l77.5 108q8 11 15 22t15 22q8 10 7 18t-7 17l-54 86l-54 87l-35 57l-36 57q-18 28 18 28h46h27z" />
|
||||
<glyph glyph-name="x" unicode="x" horiz-adv-x="698"
|
||||
d="M107 639h73q18 0 25 -15q9 -21 21 -39.5t25.5 -36.5l27 -36.5t25.5 -37.5q6 -11 10 -10.5t11 11.5l42 66l41 67q4 8 7.5 13.5t8.5 9t13.5 5t23.5 1.5h75q14 0 32 0.5t32 -0.5q16 -1 14 -10t-10 -22l-53.5 -77l-52.5 -78l-41.5 -60.5l-41.5 -59.5q-8 -11 -7.5 -19.5
|
||||
t6.5 -19.5l21 -34t22 -34l64.5 -88.5l65.5 -87.5q1 -2 5.5 -7t8.5 -11.5t6.5 -13t0.5 -11.5q-2 -3 -12.5 -4t-18.5 -1q-36 0 -71.5 1.5t-70.5 0.5q-14 -1 -22.5 4.5t-15.5 15.5l-75 117q-2 4 -4.5 8t-5.5 8q-9 9 -16 -1l-14 -18t-14 -19l-35.5 -52l-35.5 -52q-7 -10 -19 -12
|
||||
h-72.5t-73.5 1q-7 0 -11 1q-25 6 -10 27l76.5 108l77.5 108q8 11 15 22t15 22q8 10 7 18t-7 17l-54 86l-54 87l-35 57l-36 57q-18 28 18 28h46h27z" />
|
||||
<glyph glyph-name="Y" unicode="Y" horiz-adv-x="677"
|
||||
d="M169 637q8 0 12 -3.5t8 -9.5q23 -34 46 -77.5t46 -78.5l5.5 -7.5l5.5 -7.5q8 -11 18 0l9 12q12 14 30 39l34.5 49l28.5 42t13 19q9 18 20.5 21t28.5 3q13 0 33 -0.5t39.5 -0.5h34.5h18q7 -2 9.5 -8t-2.5 -13q-2 -5 -5.5 -10t-7.5 -9q-20 -23 -39.5 -50.5l-40 -56
|
||||
l-40.5 -55.5t-39 -50l-44 -50q-20 -20 -19 -50q4 -60 2.5 -130t2.5 -130q0 -11 -4 -17t-16 -7q-30 -2 -57 -1.5t-56 -0.5q-14 0 -18.5 5.5t-6.5 19.5q0 9 -0.5 33.5t-1 54t-0.5 57.5v43q0 13 -1 30t-4 34t-8 32.5t-14 27.5q-23 32 -42.5 65.5t-40.5 65.5q-20 29 -40.5 66.5
|
||||
t-40.5 66.5q-3 5 -7 8.5t-7 8.5q-6 8 -2.5 13.5t13.5 6.5h22t41 -0.5t46 -0.5t38 1z" />
|
||||
<glyph glyph-name="y" unicode="y" horiz-adv-x="677"
|
||||
d="M169 637q8 0 12 -3.5t8 -9.5q23 -34 46 -77.5t46 -78.5l5.5 -7.5l5.5 -7.5q8 -11 18 0l9 12q12 14 30 39l34.5 49l28.5 42t13 19q9 18 20.5 21t28.5 3q13 0 33 -0.5t39.5 -0.5h34.5h18q7 -2 9.5 -8t-2.5 -13q-2 -5 -5.5 -10t-7.5 -9q-20 -23 -39.5 -50.5l-40 -56
|
||||
l-40.5 -55.5t-39 -50l-44 -50q-20 -20 -19 -50q4 -60 2.5 -130t2.5 -130q0 -11 -4 -17t-16 -7q-30 -2 -57 -1.5t-56 -0.5q-14 0 -18.5 5.5t-6.5 19.5q0 9 -0.5 33.5t-1 54t-0.5 57.5v43q0 13 -1 30t-4 34t-8 32.5t-14 27.5q-23 32 -42.5 65.5t-40.5 65.5q-20 29 -40.5 66.5
|
||||
t-40.5 66.5q-3 5 -7 8.5t-7 8.5q-6 8 -2.5 13.5t13.5 6.5h22t41 -0.5t46 -0.5t38 1z" />
|
||||
<glyph glyph-name="Z" unicode="Z" horiz-adv-x="666"
|
||||
d="M521 635q8 0 15 -0.5t14 -1.5q14 -2 18 -6t3 -18q0 -11 0.5 -26t0.5 -31t-0.5 -31t-1.5 -26q1 -23 -22 -50t-55 -68q-17 -21 -47 -52.5l-61 -62.5l-58 -56.5l-38 -35.5q-6 -4 -6 -12q0 -5 5.5 -5h9.5q57 -1 114 -0.5t115 -0.5h9t9 -1q16 -3 20 -8t4 -21q1 -28 0.5 -53.5
|
||||
t1.5 -53.5q1 -10 -3 -13t-13 -3q-51 2 -100 0t-99 -1q-78 2 -155 2t-155 2q-6 0 -11.5 0.5t-10.5 1.5q-16 2 -16 17q-1 60 0 118q0 6 5 12.5t10 10.5q10 8 31 29l48.5 49l58 60l59.5 62l54.5 54.5t42.5 38.5l4.5 4.5t2.5 6.5t-5 4h-8q-69 2 -136.5 0.5t-136.5 -0.5
|
||||
q-21 0 -26.5 4.5t-5.5 26.5q-1 28 -0.5 55.5t1.5 54.5q2 16 7 21.5t18 4.5q22 -2 61 -2h87t100 1t99 1t85 -0.5t57 -1.5z" />
|
||||
<glyph glyph-name="z" unicode="z" horiz-adv-x="667"
|
||||
d="M521 635q8 0 15 -0.5t14 -1.5q14 -2 18 -6t3 -18q0 -11 0.5 -26t0.5 -31t-0.5 -31t-1.5 -26q1 -23 -22 -50t-55 -68q-17 -21 -47 -52.5l-61 -62.5l-58 -56.5l-38 -35.5q-6 -4 -6 -12q0 -5 5.5 -5h9.5q57 -1 114 -0.5t115 -0.5h9t9 -1q16 -3 20 -8t4 -21q1 -28 0.5 -53.5
|
||||
t1.5 -53.5q1 -10 -3 -13t-13 -3q-51 2 -100 0t-99 -1q-78 2 -155 2t-155 2q-6 0 -11.5 0.5t-10.5 1.5q-16 2 -16 17q-1 60 0 118q0 6 5 12.5t10 10.5q10 8 31 29l48.5 49l58 60l59.5 62l54.5 54.5t42.5 38.5l4.5 4.5t2.5 6.5t-5 4h-8q-69 2 -136.5 0.5t-136.5 -0.5
|
||||
q-21 0 -26.5 4.5t-5.5 26.5q-1 28 -0.5 55.5t1.5 54.5q2 16 7 21.5t18 4.5q22 -2 61 -2h87t100 1t99 1t85 -0.5t57 -1.5z" />
|
||||
<glyph glyph-name="guillemotleft" unicode="«" horiz-adv-x="537"
|
||||
d="M434 411h12.5t18.5 -1q4 0 8 -1.5t8 -2.5q-2 -4 -3 -8t-3 -7l-21 -26.5l-21 -25.5l-47.5 -55l-48.5 -54q-15 -18 -4 -38l96 -156q4 -7 9.5 -13.5t0.5 -14.5q-4 -5 -12 -7t-16 -3q-15 -2 -30 -2h-29q-4 0 -8.5 2t-6.5 5q-12 32 -33 58t-38 55q-13 23 -27.5 44l-28.5 42
|
||||
q-5 8 -5.5 13.5t6.5 13.5l71 86l71 85q5 6 14 8.5t17 4.5q11 1 22 0t28 -2zM125 -1q-12 -3 -19 2.5t-11 14.5q-17 42 -40 80.5l-45 77.5q-8 16 -7.5 27.5t10.5 25.5q26 38 50 77.5l47 79.5q9 16 21 24.5t32 6.5q16 -2 31.5 0.5t31.5 1.5q28 0 13 -21l-33.5 -50l-33.5 -49
|
||||
l-21.5 -34.5l-21.5 -34.5q-14 -21 -1 -46l40 -74.5l40 -74.5l6 -15t2 -12q-2 -4 -7.5 -5t-10.5 -3q-1 0 -11 0.5t-23 1t-24.5 0.5t-14.5 -1z" />
|
||||
<glyph glyph-name="guillemotright" unicode="»" horiz-adv-x="457"
|
||||
d="M50 -2q-7 0 -13 0.5t-12 1.5q-18 1 -19.5 3.5t6.5 19.5l34 78l34 77q6 12 5 21.5t-8 20.5q-17 32 -33 65.5l-33 66.5q-6 13 -3.5 16.5t17.5 4.5q20 2 39 2.5t38 -0.5q4 0 8.5 -4t6.5 -8l29 -58.5l29 -58.5q6 -12 14 -24.5t8 -25.5q1 -13 -7 -26t-13 -26
|
||||
q-11 -27 -23 -53.5t-22 -53.5q-9 -25 -25 -33.5t-40 -6.5q-5 1 -9 1h-8zM259 -3q-10 -1 -19.5 -1t-19.5 -1q-38 -3 -22 31l33 70.5l33 70.5q10 18 11 34.5t-9 35.5q-12 21 -23 43l-22 45l-6 10.5l-6 11.5q-9 23 16 26q14 2 26.5 2t26.5 2q12 2 20.5 -2t13.5 -16l36 -76.5
|
||||
l37 -76.5q4 -9 1.5 -15t-5.5 -14l-35.5 -81l-34.5 -81q-5 -11 -10.5 -16t-17.5 -3q-6 1 -15 0.5t-9 0.5z" />
|
||||
<glyph glyph-name="dollar" unicode="$" horiz-adv-x="584"
|
||||
d="M7 412q-1 49 -1.5 97t-1.5 97q0 16 5 22t21 7q11 1 22.5 0.5t23.5 -0.5q42 -2 84.5 -3.5t84.5 -3.5l49.5 -2t49.5 -4q29 -3 56 -14t47 -35q26 -32 41 -68.5t5 -80.5l-9.5 -41.5t-12.5 -40.5q-37 -81 -127 -96l-70 -10t-71 0q-3 1 -6.5 0.5t-7.5 -0.5q-13 0 -13 -13v-21
|
||||
q0 -18 17 -18q21 -1 42.5 -1.5t42.5 -1.5q19 -1 22 -4.5t4 -21.5q1 -17 0.5 -27t-5.5 -14.5t-14.5 -5.5t-26.5 -1h-70q-17 -2 -16 -19q1 -15 1.5 -30t0.5 -30q0 -17 -6 -24t-22 -7q-29 1 -58 1.5t-58 1.5q-15 0 -17.5 3t-3.5 17l-2 77t0 78q0 30 0.5 70.5t0.5 77.5v63t-1 26
|
||||
zM176 442q1 -14 0.5 -26.5t1.5 -26.5q0 -12 5 -16t17 -4q10 1 19.5 1t18.5 2l39.5 10t34.5 21q15 15 17 34.5t-13 34.5q-8 9 -19.5 17t-23.5 11q-20 4 -40 4t-41 1q-16 1 -16 -18v-28v-17z" />
|
||||
<glyph glyph-name="underscore" unicode="_" horiz-adv-x="494"
|
||||
d="M274 -143q24 1 36.5 1t22.5 0.5t22 0.5t35 1q15 0 18 -2.5t4 -16.5q2 -17 2 -33t1 -33q0 -18 -16 -19l-30 -2t-30 -1q-26 0 -38.5 -0.5t-22.5 -1.5t-22 -1.5t-38 -0.5q-11 0 -34.5 0.5t-50 1t-50 0.5t-33.5 -1q-19 -1 -26.5 4.5t-11.5 23.5q-3 16 -4.5 30t-3.5 31
|
||||
q-2 11 2.5 14.5t16.5 4.5q32 0 60.5 0.5t61.5 0.5q7 0 25.5 -0.5t39.5 -0.5t39.5 -0.5t24.5 -0.5z" />
|
||||
<glyph glyph-name="at" unicode="@" horiz-adv-x="698"
|
||||
d="M12 176q1 60 25.5 109t66.5 91q36 35 79 57.5t95 28.5q7 1 14 1t14 2q38 6 79.5 -2t79 -24t66.5 -36t41 -37q30 -43 38 -86.5t1 -82.5t-25 -71t-39 -51q-33 -30 -73 -33.5t-75 23.5q-10 8 -18 9.5t-20 -2.5q-27 -11 -55 -10t-54 15q-8 5 -16.5 11t-12.5 14
|
||||
q-9 22 -14.5 45t1.5 48q1 3 1.5 6.5t2.5 6.5q28 93 131 104q22 3 40.5 -1.5t37.5 -14.5q23 -13 19 -37q-5 -23 -0.5 -44.5t9.5 -42.5q2 -5 3 -9t3 -8q5 -8 12 -10.5t15 2.5t15.5 12t11.5 15q5 11 7.5 23.5t0.5 24.5q-8 63 -44.5 102t-103.5 44q-135 12 -211 -102
|
||||
q-7 -11 -11.5 -23.5t-8.5 -25.5q-11 -48 0 -91t43 -81q22 -26 51 -39.5t64 -8.5q21 2 31 2.5t15 -4t8.5 -14l9.5 -26.5q4 -12 6 -24q2 -14 -3 -22t-19 -13q-100 -35 -192 25q-41 26 -69.5 61t-49.5 77q-5 9 -9 23.5t-7 31t-5 33t-2 29.5z" />
|
||||
<glyph glyph-name="percent" unicode="%" horiz-adv-x="698"
|
||||
d="M180 -8q-18 1 -35.5 1t-34.5 1t-10 16l73 149q11 20 21 40.5l20 41.5l50.5 102.5l49.5 103.5l23.5 49.5l22.5 49.5q5 10 12.5 15.5t20.5 4.5q27 -1 53.5 0t52.5 1q12 1 14.5 -1.5t-2.5 -12.5l-24.5 -55l-25.5 -54l-70 -144.5l-70 -144.5q-17 -35 -35.5 -69t-35.5 -69
|
||||
q-13 -27 -42 -24q-7 1 -17.5 0.5t-10.5 -1.5zM379 120q1 34 12.5 57.5t29.5 38t42.5 21t52.5 5.5q28 -2 50.5 -17.5t34.5 -43.5q20 -48 6 -91.5t-48 -70.5q-47 -36 -100 -13q-5 2 -13 6q-40 18 -54.5 41t-12.5 67zM533 131q0 14 -11 23.5t-27 8.5q-12 -1 -23.5 -12
|
||||
t-10.5 -22q0 -15 13.5 -27t28.5 -12q13 0 21.5 12t8.5 29zM4 482q3 42 21.5 63t60.5 32q25 8 46 2.5t42 -19.5q18 -12 30 -29t16.5 -36.5t2 -40.5t-12.5 -40q-12 -21 -28.5 -35t-35.5 -21t-39.5 -5.5t-40.5 12.5q-31 16 -49 45t-13 72zM149 469q2 29 -29 31
|
||||
q-15 1 -26.5 -7.5t-10.5 -20.5q0 -11 11 -22t23 -12q11 -1 21 8.5t11 22.5z" />
|
||||
<glyph glyph-name="numbersign" unicode="#" horiz-adv-x="604"
|
||||
d="M298 1h-11q-14 0 -22 1.5t-11 5.5t-2 12t5 22l6 22l6 22q3 16 -12 17q-1 0 -1.5 0.5t-1.5 0.5q-22 2 -34.5 2t-20.5 -5t-13.5 -16.5t-12.5 -31.5q0 -1 -0.5 -1.5t-0.5 -2.5q-6 -11 -11.5 -23l-10.5 -24q-20 0 -39 -0.5t-38 0.5q-7 0 -10 5t-1 13q4 12 7.5 24t6.5 24
|
||||
q4 14 0.5 18t-18.5 6q-10 1 -19.5 0.5t-19.5 -0.5q-9 1 -13.5 4.5t-2.5 13.5q2 8 3.5 17t4.5 17l9.5 25.5t9.5 14t16 5.5t27 2q15 1 23.5 2.5t13.5 6t7 13t4 22.5q1 11 -1.5 14.5t-13.5 3.5h-46q-16 0 -11 17l10 30l10 30q6 19 27 18q9 -1 18 -0.5t17 0.5q18 1 22.5 4
|
||||
t10.5 20q12 35 21 74q2 7 5 11t11 4q20 0 41 0.5t41 0.5q14 0 9 -14l-24 -81q-4 -14 9 -13h28.5t28.5 2q20 1 24.5 5.5t10.5 24.5l10 34l10 34q1 3 4.5 6.5t6.5 3.5q31 1 63 0q17 0 21 -5t-1 -22q-5 -16 -9 -32t-9 -32q-2 -9 0 -12t12 -3q11 1 21 2.5t21 1.5q26 1 19 -24
|
||||
q-3 -9 -8 -19l-24 -54q-14 -1 -28 -1.5t-28 -1.5q-17 0 -21 -2.5t-10 -18.5q-1 -2 -1 -5t-1 -5q-6 -20 -3.5 -23t22.5 -3h39q23 0 16 -22q-5 -15 -11 -29.5t-11 -30.5q-7 -22 -31 -21q-12 0 -24.5 -1t-24.5 0q-17 0 -21 -15q-4 -14 -7.5 -29l-6.5 -29q-2 -10 -4 -15.5
|
||||
t-5.5 -8.5t-9.5 -3.5t-16 -0.5h-21v1zM259 199q10 0 19.5 -0.5t19.5 0.5q3 0 7 1t5 3q3 12 7 24t3 23q-1 9 -10 8.5t-17 -0.5q-10 1 -19.5 0.5t-19.5 0.5q-21 3 -28 -19q-5 -16 -7 -25t1 -12.5t12.5 -4t26.5 0.5z" />
|
||||
<glyph glyph-name="ampersand" unicode="&" horiz-adv-x="677"
|
||||
d="M3 175q-1 32 7 63t32 56q14 14 29.5 26t31.5 23q15 10 4 22q-30 38 -35 81t3 88q4 25 20 43.5t38 32.5q35 22 72.5 26.5t77.5 -6.5q34 -10 55 -32t35 -52q12 -27 14.5 -57.5t-3.5 -60.5t-20.5 -56t-36.5 -43q-16 -11 -16 -17t14 -21q7 -8 14 -14.5t13 -14.5q12 -14 24 0
|
||||
l11 17l14 22.5t12 21t6 12.5q5 20 29 19q21 -1 43 0t42 -1q14 -1 14 -15q-2 -48 -20.5 -91t-43.5 -81q-11 -15 -10.5 -20t12.5 -18l39 -40.5l39 -41.5q2 -2 5 -5.5t5 -5.5q5 -5 9 -11t0 -14q-3 -8 -9.5 -8.5t-14.5 -0.5h-62.5t-62.5 1q-3 0 -7 3t-8.5 7t-8.5 8l-6 6
|
||||
q-13 15 -31 4q-53 -32 -117 -46q-60 -13 -110 3.5t-93 54.5q-29 25 -40.5 59t-8.5 74zM227 132q8 0 16.5 3t17.5 7q9 3 10.5 9t-3.5 13l-27.5 32.5l-27.5 32.5q-8 8 -19 1q-17 -11 -24.5 -29.5t-2.5 -34.5q5 -13 25.5 -23.5t34.5 -10.5zM271 470q0 17 -6 26.5t-14 14
|
||||
t-16.5 4t-13.5 -2.5q-10 -4 -15.5 -12t-2.5 -19q5 -17 11.5 -32.5t14.5 -31.5q1 -2 5 -3t6 0q13 9 22 25.5t9 30.5z" />
|
||||
<glyph glyph-name="quotedblleft" unicode="“" horiz-adv-x="537"
|
||||
d="M434 411h12.5t18.5 -1q4 0 8 -1.5t8 -2.5q-2 -4 -3 -8t-3 -7l-21 -26.5l-21 -25.5l-47.5 -55l-48.5 -54q-15 -18 -4 -38l96 -156q4 -7 9.5 -13.5t0.5 -14.5q-4 -5 -12 -7t-16 -3q-15 -2 -30 -2h-29q-4 0 -8.5 2t-6.5 5q-12 32 -33 58t-38 55q-13 23 -27.5 44l-28.5 42
|
||||
q-5 8 -5.5 13.5t6.5 13.5l71 86l71 85q5 6 14 8.5t17 4.5q11 1 22 0t28 -2zM125 -1q-12 -3 -19 2.5t-11 14.5q-17 42 -40 80.5l-45 77.5q-8 16 -7.5 27.5t10.5 25.5q26 38 50 77.5l47 79.5q9 16 21 24.5t32 6.5q16 -2 31.5 0.5t31.5 1.5q28 0 13 -21l-33.5 -50l-33.5 -49
|
||||
l-21.5 -34.5l-21.5 -34.5q-14 -21 -1 -46l40 -74.5l40 -74.5l6 -15t2 -12q-2 -4 -7.5 -5t-10.5 -3q-1 0 -11 0.5t-23 1t-24.5 0.5t-14.5 -1z" />
|
||||
<glyph glyph-name="quotedblright" unicode="”" horiz-adv-x="448"
|
||||
d="M50 -2q-7 0 -13 0.5t-12 1.5q-18 1 -19.5 3.5t6.5 19.5l34 78l34 77q6 12 5 21.5t-8 20.5q-17 32 -33 65.5l-33 66.5q-6 13 -3.5 16.5t17.5 4.5q20 2 39 2.5t38 -0.5q4 0 8.5 -4t6.5 -8l29 -58.5l29 -58.5q6 -12 14 -24.5t8 -25.5q1 -13 -7 -26t-13 -26
|
||||
q-11 -27 -23 -53.5t-22 -53.5q-9 -25 -25 -33.5t-40 -6.5q-5 1 -9 1h-8zM259 -3q-10 -1 -19.5 -1t-19.5 -1q-38 -3 -22 31l33 70.5l33 70.5q10 18 11 34.5t-9 35.5q-12 21 -23 43l-22 45l-6 10.5l-6 11.5q-9 23 16 26q14 2 26.5 2t26.5 2q12 2 20.5 -2t13.5 -16l36 -76.5
|
||||
l37 -76.5q4 -9 1.5 -15t-5.5 -14l-35.5 -81l-34.5 -81q-5 -11 -10.5 -16t-17.5 -3q-6 1 -15 0.5t-9 0.5z" />
|
||||
<glyph glyph-name="less" unicode="<" horiz-adv-x="457"
|
||||
d="M391 398q-1 -14 -1.5 -26.5t-2.5 -26.5l-54 -24q-39 -17 -78.5 -32.5l-78.5 -31.5q-4 -2 -7 -5t-7 -5q3 -3 5.5 -6t6.5 -5q50 -30 101.5 -57t105.5 -50q25 -11 22 -38q-2 -19 -2 -37t-1 -37q-2 -17 -17 -11l-21 10l-21 10l-99 49.5l-100 50.5l-57.5 30.5l-57.5 30.5
|
||||
q-25 11 -23 39l2 30t0 30q-1 20 18 27l98.5 43.5l99.5 42.5l73.5 29.5l73.5 29.5q14 6 19 3t5 -17v-13t-0.5 -15t-1 -12.5z" />
|
||||
<glyph glyph-name="greater" unicode=">" horiz-adv-x="473"
|
||||
d="M6 119q-5 15 3 23.5t22 14.5l77 34.5l77 34.5q7 3 13.5 5.5t12.5 6.5q3 2 4.5 4.5t3.5 4.5q-5 5 -8 6q-26 11 -56 21q-38 11 -72 30t-69 36q-2 2 -4.5 6t-2.5 8q-1 23 -1 44.5t-1 44.5q0 15 18 10l54.5 -19.5t55.5 -20.5l88.5 -34.5l88.5 -34.5l31 -13.5t31 -12.5
|
||||
q10 -4 12.5 -10.5t2.5 -14.5q1 -18 0.5 -35.5t1.5 -34.5q1 -15 -4 -24.5t-19 -14.5l-35.5 -16l-35.5 -16l-112 -52.5l-112 -52.5q-7 -3 -13 -6l-12 -6q-17 -8 -24 -4t-10 22q-2 16 -3 32t-3 34z" />
|
||||
<glyph glyph-name="quotedblbase" unicode="„" horiz-adv-x="833"
|
||||
/>
|
||||
<glyph glyph-name="quotedbl" unicode=""" horiz-adv-x="833"
|
||||
/>
|
||||
<glyph glyph-name="uni0406" unicode="І" horiz-adv-x="302"
|
||||
d="M198 248v-226q0 -16 -4.5 -19.5t-21.5 -1.5q-33 4 -66 1.5t-69 -1.5q-31 -5 -31 26q-1 18 -0.5 34t0.5 35q1 45 1 90v90q0 68 -0.5 144t-1.5 144q0 12 0.5 24t1.5 24q2 14 5.5 17.5t16.5 5.5q38 4 74 3.5t73 -5.5q11 -1 18 -2t8 -12q2 -10 1.5 -25t-0.5 -25
|
||||
q-1 -66 -2 -148.5t-3 -172.5z" />
|
||||
<glyph glyph-name="uni0456" unicode="і" horiz-adv-x="302"
|
||||
d="M198 248v-226q0 -16 -4.5 -19.5t-21.5 -1.5q-33 4 -66 1.5t-69 -1.5q-31 -5 -31 26q-1 18 -0.5 34t0.5 35q1 45 1 90v90q0 68 -0.5 144t-1.5 144q0 12 0.5 24t1.5 24q2 14 5.5 17.5t16.5 5.5q38 4 74 3.5t73 -5.5q11 -1 18 -2t8 -12q2 -10 1.5 -25t-0.5 -25
|
||||
q-1 -66 -2 -148.5t-3 -172.5z" />
|
||||
<glyph glyph-name="uni0490" unicode="Ґ" horiz-adv-x="542"
|
||||
d="M8 293v50t-0.5 61l-1 64t-1 58.5t-1 44.5t-0.5 23v15t1 15q0 17 19 18q18 0 44.5 0.5t58.5 0.5h67h69v28.5v26.5t0.5 20t0.5 11q0 17 19 18q31 1 48.5 1h29.5t22 -0.5t27 -0.5q18 0 22 -4t6 -22q0 -3 0.5 -19t1 -39t1 -49.5t1 -50t1 -41.5t1.5 -23q2 -16 -2 -21.5
|
||||
t-19 -7.5q-6 -1 -33 -1.5t-61.5 -0.5t-69 -0.5t-56.5 -0.5q-16 0 -16 -17q0 -4 -0.5 -7.5t-0.5 -7.5q1 -75 0 -156t1 -156q1 -23 0.5 -40t0.5 -40q1 -14 -2 -31q-3 -15 -19 -15q-38 -2 -72.5 -2.5t-71.5 -1.5q-8 0 -10.5 4t-3.5 10q-1 7 -1 13.5v13.5v130t-1 129h1z" />
|
||||
<glyph glyph-name="uni0491" unicode="ґ" horiz-adv-x="542"
|
||||
d="M8 293v50t-0.5 61l-1 64t-1 58.5t-1 44.5t-0.5 23v15t1 15q0 17 19 18q18 0 44.5 0.5t58.5 0.5h67h69v28.5v26.5t0.5 20t0.5 11q0 17 19 18q31 1 48.5 1h29.5t22 -0.5t27 -0.5q18 0 22 -4t6 -22q0 -3 0.5 -19t1 -39t1 -49.5t1 -50t1 -41.5t1.5 -23q2 -16 -2 -21.5
|
||||
t-19 -7.5q-6 -1 -33 -1.5t-61.5 -0.5t-69 -0.5t-56.5 -0.5q-16 0 -16 -17q0 -4 -0.5 -7.5t-0.5 -7.5q1 -75 0 -156t1 -156q1 -23 0.5 -40t0.5 -40q1 -14 -2 -31q-3 -15 -19 -15q-38 -2 -72.5 -2.5t-71.5 -1.5q-8 0 -10.5 4t-3.5 10q-1 7 -1 13.5v13.5v130t-1 129h1z" />
|
||||
<glyph glyph-name="uni0407" unicode="Ї" horiz-adv-x="302"
|
||||
d="M9 719q-16 1 -29.5 7t-23.5 16.5t-15.5 23.5t-4.5 28q1 13 7.5 25t16.5 21t22.5 14.5t25.5 5.5q12 0 24.5 -6.5t22 -17t15.5 -23t6 -24.5q0 -15 -5.5 -28t-14.5 -22.5t-21.5 -15t-25.5 -4.5zM196 855q12 0 23.5 -5t20.5 -13.5t14 -19t5 -22.5q-1 -30 -18.5 -49.5
|
||||
t-44.5 -18.5q-29 0 -48 19.5t-19 49.5q-1 26 18 42.5t49 16.5zM198 248v-226q0 -16 -4.5 -19.5t-21.5 -1.5q-33 4 -66 1.5t-69 -1.5q-31 -5 -31 26q-1 18 -0.5 34t0.5 35q1 45 1 90v90q0 68 -0.5 144t-1.5 144q0 12 0.5 24t1.5 24q2 14 5.5 17.5t16.5 5.5q38 4 74 3.5
|
||||
t73 -5.5q11 -1 18 -2t8 -12q2 -10 1.5 -25t-0.5 -25q-1 -66 -2 -148.5t-3 -172.5z" />
|
||||
<glyph glyph-name="uni0457" unicode="ї" horiz-adv-x="302"
|
||||
d="M9 719q-16 1 -29.5 7t-23.5 16.5t-15.5 23.5t-4.5 28q1 13 7.5 25t16.5 21t22.5 14.5t25.5 5.5q12 0 24.5 -6.5t22 -17t15.5 -23t6 -24.5q0 -15 -5.5 -28t-14.5 -22.5t-21.5 -15t-25.5 -4.5zM196 855q12 0 23.5 -5t20.5 -13.5t14 -19t5 -22.5q-1 -30 -18.5 -49.5
|
||||
t-44.5 -18.5q-29 0 -48 19.5t-19 49.5q-1 26 18 42.5t49 16.5zM198 248v-226q0 -16 -4.5 -19.5t-21.5 -1.5q-33 4 -66 1.5t-69 -1.5q-31 -5 -31 26q-1 18 -0.5 34t0.5 35q1 45 1 90v90q0 68 -0.5 144t-1.5 144q0 12 0.5 24t1.5 24q2 14 5.5 17.5t16.5 5.5q38 4 74 3.5
|
||||
t73 -5.5q11 -1 18 -2t8 -12q2 -10 1.5 -25t-0.5 -25q-1 -66 -2 -148.5t-3 -172.5z" />
|
||||
<glyph glyph-name="uni0404" unicode="Є" horiz-adv-x="674"
|
||||
d="M-22 300q-5 72 15 132.5t64 112.5q35 42 78.5 71.5t99.5 37.5q60 9 121 6q100 -3 178 -69q11 -10 11.5 -14t-9.5 -16q-4 -4 -7 -8.5t-6 -9.5l-23.5 -39l-24.5 -39q-8 -13 -15.5 -16.5t-24.5 5.5q-81 44 -173 18q-27 -8 -49.5 -23t-32.5 -44q-4 -13 -2 -16.5t16 -5.5
|
||||
q9 0 33.5 -0.5t52 -0.5t52 -0.5t33.5 -0.5q21 -1 28.5 -8.5t6.5 -28.5q0 -11 -2 -21t-2 -21q0 -21 -20 -21q-8 -1 -17 -1h-17h-29.5t-48.5 0.5t-48.5 0.5h-28.5q-20 0 -14 -20q14 -46 55 -64q52 -23 103 -23.5t100 32.5q17 11 32 24t29 27q16 16 34 7q12 -6 17 -19l27 -64.5
|
||||
l27 -65.5q3 -8 -0.5 -12.5t-8.5 -9.5q-43 -40 -95 -62q-67 -29 -135.5 -40.5t-141.5 11.5q-41 13 -75.5 32.5t-68.5 44.5q-33 24 -54 62t-31 78.5t-9 80.5z" />
|
||||
<glyph glyph-name="uni0454" unicode="є" horiz-adv-x="674"
|
||||
d="M-22 300q-5 72 15 132.5t64 112.5q35 42 78.5 71.5t99.5 37.5q60 9 121 6q100 -3 178 -69q11 -10 11.5 -14t-9.5 -16q-4 -4 -7 -8.5t-6 -9.5l-23.5 -39l-24.5 -39q-8 -13 -15.5 -16.5t-24.5 5.5q-81 44 -173 18q-27 -8 -49.5 -23t-32.5 -44q-4 -13 -2 -16.5t16 -5.5
|
||||
q9 0 33.5 -0.5t52 -0.5t52 -0.5t33.5 -0.5q21 -1 28.5 -8.5t6.5 -28.5q0 -11 -2 -21t-2 -21q0 -21 -20 -21q-8 -1 -17 -1h-17h-29.5t-48.5 0.5t-48.5 0.5h-28.5q-20 0 -14 -20q14 -46 55 -64q52 -23 103 -23.5t100 32.5q17 11 32 24t29 27q16 16 34 7q12 -6 17 -19l27 -64.5
|
||||
l27 -65.5q3 -8 -0.5 -12.5t-8.5 -9.5q-43 -40 -95 -62q-67 -29 -135.5 -40.5t-141.5 11.5q-41 13 -75.5 32.5t-68.5 44.5q-33 24 -54 62t-31 78.5t-9 80.5z" />
|
||||
<glyph glyph-name="uni20B4" unicode="₴" horiz-adv-x="583"
|
||||
d="M261 271l-10 -6q-16 -9 -31 -19.5t-28 -23.5q-11 -11 -14 -24t0.5 -25t13.5 -21t25 -12q8 -2 15.5 -0.5t14.5 1.5q36 -1 65 6t57 21q14 7 23.5 12.5t20.5 16.5q5 5 10 3.5t8 -6.5q5 -7 8 -14t7 -15q10 -22 16 -36.5t11 -35.5q3 -11 5 -17.5t2 -11.5t-3.5 -9.5
|
||||
t-12.5 -12.5q-6 -6 -12 -9q-33 -17 -62 -27t-72 -15q-26 -3 -47 -5t-41.5 -1.5t-42.5 3.5t-50 9q-60 14 -92 57.5t-39 103.5q-4 31 0.5 59t24.5 54l5 7zM187 395l17 9l33.5 18.5t32.5 20.5q12 9 14.5 25.5t-3.5 28.5q-7 14 -20 15.5t-31 1.5q-22 0 -42 -5.5t-35 -12.5
|
||||
t-24.5 -13.5t-11.5 -7.5q-12 -7 -20 2q-9 9 -15 18.5t-11.5 19.5l-11 20t-14.5 20q-5 5 -7 13t4 14q19 19 34.5 32t41.5 24q14 6 39.5 12t58 7.5t71.5 -5.5t80 -27q30 -15 44 -28.5t28 -33.5q36 -50 21 -112q-5 -19 -12 -34.5t-18 -28.5zM491 443q15 0 18 -2.5t4 -16.5
|
||||
q1 -9 1.5 -12.5t0.5 -5.5t0.5 -5.5t0.5 -12.5q0 -18 -16 -19l-30 -2t-30 -1h-54t-81 0.5l-93 1t-89.5 1t-70.5 0.5h-36q-19 -1 -28 3t-10 25q0 8 -0.5 12t-0.5 14q0 11 3.5 14.5t15.5 4.5h47.5h86.5h107h108.5t91 0.5t54.5 0.5zM473 316q15 0 18 -2.5t4 -16.5
|
||||
q1 -9 1.5 -12.5t0.5 -5.5t0.5 -5.5t0.5 -12.5q0 -18 -16 -19l-30 -2t-30 -1h-54t-81 0.5l-93 1t-89.5 1t-70.5 0.5h-36q-19 -1 -28 3t-10 25q0 8 -0.5 12t-0.5 14q0 11 3.5 14.5t15.5 4.5h47.5h86.5h107h108.5t91 0.5t54.5 0.5z" />
|
||||
<hkern u1="Г" u2="а" k="70" />
|
||||
<hkern u1="Г" u2="А" k="70" />
|
||||
<hkern u1="Т" u2="а" k="50" />
|
||||
<hkern u1="Т" u2="А" k="50" />
|
||||
<hkern u1="А" u2="Т" k="50" />
|
||||
<hkern u1="а" u2="т" k="50" />
|
||||
<hkern u1="г" u2="а" k="70" />
|
||||
<hkern u1="т" u2="а" k="50" />
|
||||
<hkern u1="A" u2="T" k="50" />
|
||||
<hkern u1="a" u2="t" k="50" />
|
||||
<hkern u1="T" u2="a" k="50" />
|
||||
<hkern u1="T" u2="A" k="50" />
|
||||
<hkern u1="t" u2="a" k="50" />
|
||||
<hkern u1="Ґ" u2="а" k="70" />
|
||||
<hkern u1="Ґ" u2="А" k="70" />
|
||||
<hkern u1="ґ" u2="а" k="70" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
Before Width: | Height: | Size: 113 KiB |
@@ -1,3 +0,0 @@
|
||||
export default function Loading() {
|
||||
return <div>loading...</div>
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
import { tail } from "@hudozka/utils"
|
||||
import type { Metadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
import { App } from "@/components/App"
|
||||
import { PageContent } from "@/components/PageContent"
|
||||
import { buildMetadata, MetaBuilder } from "@/lib/meta"
|
||||
import { getPageBySlug, getUrls } from "@/remote/api"
|
||||
|
||||
export const revalidate = 30
|
||||
export const dynamicParams = true
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const urls = await getUrls()
|
||||
return urls.map(path => ({
|
||||
slug: tail(path.split("/")),
|
||||
}))
|
||||
}
|
||||
|
||||
type Props = {
|
||||
params: Promise<{ slug: string[] }>
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const { slug } = await params
|
||||
const slugPath = `/${slug.join("/")}`
|
||||
const page = await getPageBySlug(slugPath)
|
||||
if (!page) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const m = new MetaBuilder()
|
||||
.setImage(page.cover)
|
||||
.setTitle(page.title)
|
||||
.setDescription(page.description ?? undefined)
|
||||
.build()
|
||||
return buildMetadata(m)
|
||||
}
|
||||
|
||||
export default async function SlugPage({ params }: Props) {
|
||||
const { slug } = await params
|
||||
const slugPath = `/${slug.join("/")}`
|
||||
const page = await getPageBySlug(slugPath)
|
||||
if (!page) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
const breadcrumbSize = page.breadcrumb?.length ?? 0
|
||||
const breadcrumbs = breadcrumbSize < 2 ? [] : (page.breadcrumb ?? [])
|
||||
|
||||
return (
|
||||
<App
|
||||
contentStyle={{
|
||||
marginTop: "var(--size-l)",
|
||||
marginBottom: "var(--size-xl)",
|
||||
}}
|
||||
breadcrumbs={breadcrumbs}
|
||||
>
|
||||
<PageContent
|
||||
tags={page.tags}
|
||||
date={page.date}
|
||||
tokens={page.tokens}
|
||||
documentSignature={{
|
||||
date: "20.01.2021г.",
|
||||
person: "Тимашева Марина Геннадьевна",
|
||||
position: "Директор",
|
||||
signature: "0ac4ea89753a4ba9893799442325fb41",
|
||||
}}
|
||||
/>
|
||||
</App>
|
||||
)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { NextResponse } from "next/server"
|
||||
import { siteUrl } from "@/const"
|
||||
import { getRecentPages } from "@/remote/api"
|
||||
|
||||
export async function GET() {
|
||||
const pages = await getRecentPages()
|
||||
|
||||
const feed = {
|
||||
version: "https://jsonfeed.org/version/1.1",
|
||||
title: "Шлиссельбургская ДХШ",
|
||||
home_page_url: siteUrl,
|
||||
feed_url: `${siteUrl}/feed.json`,
|
||||
items: pages.map(page => ({
|
||||
id: `${siteUrl}${page.url}`,
|
||||
url: `${siteUrl}${page.url}`,
|
||||
title: page.title,
|
||||
date_published: new Date(page.date).toISOString(),
|
||||
summary: page.excerpt,
|
||||
})),
|
||||
}
|
||||
|
||||
return NextResponse.json(feed)
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
import "src/style/style.css"
|
||||
import "src/style/content.css"
|
||||
import "src/style/article.css"
|
||||
import "src/style/marker/marker-table.css"
|
||||
import "src/style/kazimir/video.css"
|
||||
|
||||
import type { Metadata, Viewport } from "next"
|
||||
import { Roboto } from "next/font/google"
|
||||
import Script from "next/script"
|
||||
import { YMetrika } from "@/components/YMetrika"
|
||||
|
||||
const roboto = Roboto({
|
||||
weight: ["300", "900"],
|
||||
subsets: ["cyrillic", "latin"],
|
||||
display: "swap",
|
||||
})
|
||||
|
||||
export const metadata: Metadata = {
|
||||
verification: {
|
||||
google: ["8BxOTw-Q02DS2KAdcGcnIMwiPZ58DVB18XpOfRibCcg", "A-TEemUw4hHpULUDCd6xhxfHnyRbgKtss90JGKXnSpw"],
|
||||
other: {
|
||||
"yandex-verification": "643c7dac144b9dac",
|
||||
"msvalidate.01": "E878301FE3E3F12D233A6B1156889601",
|
||||
"proculture-verification": "79110940fd847c3cfe52392ed68f3210",
|
||||
},
|
||||
},
|
||||
icons: {
|
||||
icon: { url: "/static/graphics/favicon-32.png", type: "image/png" },
|
||||
apple: [
|
||||
{ url: "/static/graphics/favicon-57.png", sizes: "57x57" },
|
||||
{ url: "/static/graphics/favicon-72.png", sizes: "72x72" },
|
||||
{ url: "/static/graphics/favicon-114.png", sizes: "114x114" },
|
||||
{ url: "/static/graphics/favicon-144.png", sizes: "144x144" },
|
||||
],
|
||||
},
|
||||
other: {
|
||||
"apple-mobile-web-app-capable": "yes",
|
||||
referrer: "unsafe-url",
|
||||
},
|
||||
alternates: {
|
||||
types: {
|
||||
"application/feed+json": "/feed.json",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const viewport: Viewport = {
|
||||
width: "device-width",
|
||||
maximumScale: 1,
|
||||
themeColor: [
|
||||
{ media: "(prefers-color-scheme: light)", color: "white" },
|
||||
{ media: "(prefers-color-scheme: dark)", color: "hsl(240, 10%, 10%)" },
|
||||
],
|
||||
}
|
||||
|
||||
export const revalidate = 30
|
||||
|
||||
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="ru" className={roboto.className}>
|
||||
<head>
|
||||
{process.env.NEXT_PUBLIC_YMETRIKA_ACCOUNT && (
|
||||
<YMetrika account={Number(process.env.NEXT_PUBLIC_YMETRIKA_ACCOUNT)} />
|
||||
)}
|
||||
</head>
|
||||
<body>
|
||||
{children}
|
||||
<Script src="https://culturaltracking.ru/static/js/spxl.js?pixelId=27154" data-pixel-id="27154" />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { App } from "@/components/App"
|
||||
import { NotFound } from "@/components/NotFound"
|
||||
|
||||
export default function NotFoundPage() {
|
||||
return (
|
||||
<App>
|
||||
<NotFound />
|
||||
</App>
|
||||
)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { Metadata } from "next"
|
||||
import { App } from "@/components/App"
|
||||
import { HomeContent } from "@/components/HomeContent"
|
||||
import { buildMetadata, MetaBuilder } from "@/lib/meta"
|
||||
import { getHomeCards } from "@/remote/api"
|
||||
|
||||
export const revalidate = 30
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const title = "Шлиссельбургская ДХШ"
|
||||
const m = new MetaBuilder().setTitle(title).setData({ url: "/" }).build()
|
||||
return buildMetadata(m)
|
||||
}
|
||||
|
||||
export default async function HomePage() {
|
||||
const items = await getHomeCards()
|
||||
|
||||
return (
|
||||
<App showAuthor>
|
||||
<HomeContent items={items} />
|
||||
</App>
|
||||
)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import type { MetadataRoute } from "next"
|
||||
import { siteUrl } from "@/const"
|
||||
|
||||
export default function robots(): MetadataRoute.Robots {
|
||||
return {
|
||||
rules: {
|
||||
userAgent: "*",
|
||||
allow: "/",
|
||||
},
|
||||
host: siteUrl,
|
||||
sitemap: `${siteUrl}/sitemap.xml`,
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import type { MetadataRoute } from "next"
|
||||
import { siteUrl } from "@/const"
|
||||
import { getAllTagSlugs, getUrls } from "@/remote/api"
|
||||
|
||||
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||
const [urls, tagSlugs] = await Promise.all([getUrls(), getAllTagSlugs()])
|
||||
|
||||
const staticPages: MetadataRoute.Sitemap = [
|
||||
{
|
||||
url: siteUrl,
|
||||
changeFrequency: "daily",
|
||||
},
|
||||
{
|
||||
url: `${siteUrl}/collective`,
|
||||
changeFrequency: "daily",
|
||||
},
|
||||
{
|
||||
url: `${siteUrl}/tags`,
|
||||
changeFrequency: "weekly",
|
||||
},
|
||||
]
|
||||
|
||||
const dynamicPages: MetadataRoute.Sitemap = urls.map(path => ({
|
||||
url: `${siteUrl}${path}`,
|
||||
changeFrequency: "daily",
|
||||
}))
|
||||
|
||||
const tagPages: MetadataRoute.Sitemap = tagSlugs.map(slug => ({
|
||||
url: `${siteUrl}/tags/${slug}`,
|
||||
changeFrequency: "weekly",
|
||||
}))
|
||||
|
||||
return [...staticPages, ...dynamicPages, ...tagPages]
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import { Title } from "@hudozka/ui"
|
||||
import type { Metadata } from "next"
|
||||
import { notFound } from "next/navigation"
|
||||
import { App } from "@/components/App"
|
||||
import { PageGrid } from "@/components/PageGrid"
|
||||
import { Pagination } from "@/components/Pagination"
|
||||
import { menu } from "@/const"
|
||||
import { buildMetadata, MetaBuilder } from "@/lib/meta"
|
||||
import { getAllTagSlugs, getPagesByTag } from "@/remote/api"
|
||||
|
||||
export const revalidate = 30
|
||||
export const dynamicParams = true
|
||||
|
||||
const PER_PAGE = 24
|
||||
|
||||
export async function generateStaticParams() {
|
||||
const slugs = await getAllTagSlugs()
|
||||
return slugs.map(slug => ({ slug }))
|
||||
}
|
||||
|
||||
type Props = {
|
||||
params: Promise<{ slug: string }>
|
||||
searchParams: Promise<{ page?: string }>
|
||||
}
|
||||
|
||||
function parsePage(value: string | undefined): number {
|
||||
const n = Number.parseInt(value ?? "1", 10)
|
||||
return Number.isFinite(n) && n >= 1 ? n : 1
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params, searchParams }: Props): Promise<Metadata> {
|
||||
const [{ slug }, sp] = await Promise.all([params, searchParams])
|
||||
const page = parsePage(sp.page)
|
||||
|
||||
const listing = await getPagesByTag(slug, page, PER_PAGE)
|
||||
if (!listing) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const titleBase = `${listing.tag.name} — метка`
|
||||
const title = page > 1 ? `${titleBase}, страница ${page}` : titleBase
|
||||
|
||||
const meta = new MetaBuilder().setTitle(title).setDescription(`Записи с меткой «${listing.tag.name}».`).build()
|
||||
|
||||
const metadata = buildMetadata(meta)
|
||||
if (page > 1) {
|
||||
return { ...metadata, robots: { index: false, follow: true } }
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
export default async function TagPage({ params, searchParams }: Props) {
|
||||
const [{ slug }, sp] = await Promise.all([params, searchParams])
|
||||
const page = parsePage(sp.page)
|
||||
|
||||
const listing = await getPagesByTag(slug, page, PER_PAGE)
|
||||
if (!listing) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
const totalPages = Math.max(1, Math.ceil(listing.total / listing.perPage))
|
||||
if (listing.total > 0 && page > totalPages) {
|
||||
notFound()
|
||||
}
|
||||
|
||||
const breadcrumbs = [
|
||||
{ name: menu[0].name, href: "/" },
|
||||
{ name: "Метки", href: "/tags" },
|
||||
{ name: listing.tag.name, href: listing.tag.href },
|
||||
]
|
||||
|
||||
return (
|
||||
<App
|
||||
contentStyle={{
|
||||
marginTop: "var(--size-l)",
|
||||
marginBottom: "var(--size-xl)",
|
||||
}}
|
||||
breadcrumbs={breadcrumbs}
|
||||
>
|
||||
<Title level={1} style={{ marginBottom: "var(--size-l)" }}>
|
||||
{listing.tag.name}
|
||||
</Title>
|
||||
{listing.items.length === 0 ? (
|
||||
<p>Пока нет записей.</p>
|
||||
) : (
|
||||
<>
|
||||
<PageGrid items={listing.items} />
|
||||
<Pagination
|
||||
basePath={listing.tag.href}
|
||||
page={listing.page}
|
||||
total={listing.total}
|
||||
perPage={listing.perPage}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</App>
|
||||
)
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import { Title } from "@hudozka/ui"
|
||||
import type { Metadata } from "next"
|
||||
import { App } from "@/components/App"
|
||||
import { TagsIndex } from "@/components/TagsIndex"
|
||||
import { menu } from "@/const"
|
||||
import { buildMetadata, MetaBuilder } from "@/lib/meta"
|
||||
import { getAllTagsWithCounts } from "@/remote/api"
|
||||
|
||||
export const revalidate = 30
|
||||
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const meta = new MetaBuilder().setTitle("Метки").setDescription("Список всех меток сайта.").build()
|
||||
return buildMetadata(meta)
|
||||
}
|
||||
|
||||
export default async function TagsIndexPage() {
|
||||
const tags = await getAllTagsWithCounts()
|
||||
|
||||
const breadcrumbs = [
|
||||
{ name: menu[0].name, href: "/" },
|
||||
{ name: "Метки", href: "/tags" },
|
||||
]
|
||||
|
||||
return (
|
||||
<App
|
||||
contentStyle={{
|
||||
marginTop: "var(--size-l)",
|
||||
marginBottom: "var(--size-xl)",
|
||||
}}
|
||||
breadcrumbs={breadcrumbs}
|
||||
>
|
||||
<Title level={1} style={{ marginBottom: "var(--size-l)" }}>
|
||||
Метки
|
||||
</Title>
|
||||
<TagsIndex items={tags} />
|
||||
</App>
|
||||
)
|
||||
}
|
||||
|
Before Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,14 +0,0 @@
|
||||
import { Button } from "@hudozka/ui"
|
||||
import { HiOutlineEye, HiOutlineEyeOff } from "react-icons/hi"
|
||||
import { useSnapshot } from "valtio"
|
||||
import { theme, toggleTheme } from "@/store/theme"
|
||||
|
||||
export function AccessibilityButton() {
|
||||
const t = useSnapshot(theme)
|
||||
|
||||
return (
|
||||
<Button theme="icon" size="default" onClick={toggleTheme}>
|
||||
{t.theme === "default" ? <HiOutlineEyeOff size={24} /> : <HiOutlineEye size={24} />}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
import { HiCog, HiOutlineEyeOff, HiPlay } from "react-icons/hi"
|
||||
import { useSnapshot } from "valtio"
|
||||
import play from "@/play"
|
||||
import { state as playState, setVoice } from "@/store/play"
|
||||
import {
|
||||
decreaseFontSize,
|
||||
hideImages,
|
||||
increaseFontSize,
|
||||
reset,
|
||||
setBlackOnWhite,
|
||||
setBlueOnBlue,
|
||||
setBrownOnYellow,
|
||||
setDefaultTheme,
|
||||
setGreenOnBrown,
|
||||
setLetterSpacingFour,
|
||||
setLetterSpacingOne,
|
||||
setLetterSpacingTwo,
|
||||
setLineHeightOneAndHalf,
|
||||
setLineHeightTwo,
|
||||
setLineHeightTwoAndHalf,
|
||||
setSansSerifFont,
|
||||
setSerifFont,
|
||||
setWhiteOnBlack,
|
||||
showImages,
|
||||
} from "@/store/theme"
|
||||
import s from "./panel.module.css"
|
||||
import VoiceSelect from "./voice"
|
||||
|
||||
function AccessibilityPanel() {
|
||||
const p = useSnapshot(playState)
|
||||
|
||||
return (
|
||||
<div className={s.viewsettings}>
|
||||
<div className={s.row}>
|
||||
<div className={s.block}>
|
||||
<p>Размер шрифта:</p>
|
||||
<button type="button" onClick={decreaseFontSize} title="уменьшить размер шрифта">
|
||||
A-
|
||||
</button>
|
||||
<button type="button" onClick={increaseFontSize} title="увеличить размер шрифта">
|
||||
<strong>A+</strong>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={s.block}>
|
||||
<p>Изображения:</p>
|
||||
<button type="button" onClick={showImages}>
|
||||
Включить
|
||||
</button>
|
||||
<button type="button" onClick={hideImages}>
|
||||
Выключить
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={s.block}>
|
||||
<p>Цвет сайта:</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={setBlackOnWhite}
|
||||
title="черные буквы на белом фоне"
|
||||
style={{ color: "black", backgroundColor: "white" }}
|
||||
>
|
||||
Ц
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={setWhiteOnBlack}
|
||||
title="белые буквы на черном фоне"
|
||||
style={{ color: "white", backgroundColor: "black" }}
|
||||
>
|
||||
Ц
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={setBlueOnBlue}
|
||||
title="синии буквы на голубом фоне"
|
||||
style={{ color: "oklch(32% 24% 253deg)", backgroundColor: "oklch(84% 21% 245deg)" }}
|
||||
>
|
||||
Ц
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={setBrownOnYellow}
|
||||
title="коричневые буквы на желтом фоне"
|
||||
style={{ color: "oklch(41% 3% 92deg)", backgroundColor: "oklch(96% 9% 101deg)" }}
|
||||
>
|
||||
Ц
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={setGreenOnBrown}
|
||||
title="зеленые буквы на коричневом фоне"
|
||||
style={{ color: "oklch(85% 47% 128deg)", backgroundColor: "oklch(29% 10% 61deg)" }}
|
||||
>
|
||||
Ц
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={s.block}>
|
||||
<p>Шрифт:</p>
|
||||
<button type="button" onClick={setSerifFont}>
|
||||
С засечками
|
||||
</button>
|
||||
<button type="button" onClick={setSansSerifFont}>
|
||||
Без засечек
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={s.block}>
|
||||
<p>Высота строк:</p>
|
||||
<button type="button" onClick={setLineHeightOneAndHalf}>
|
||||
1.5
|
||||
</button>
|
||||
<button type="button" onClick={setLineHeightTwo}>
|
||||
2.0
|
||||
</button>
|
||||
<button type="button" onClick={setLineHeightTwoAndHalf}>
|
||||
2.5
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={s.block}>
|
||||
<p>Межбуквенный интервал:</p>
|
||||
<button type="button" onClick={setLetterSpacingOne}>
|
||||
1
|
||||
</button>
|
||||
<button type="button" onClick={setLetterSpacingTwo}>
|
||||
2
|
||||
</button>
|
||||
<button type="button" onClick={setLetterSpacingFour}>
|
||||
4
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={s.row}>
|
||||
<button type="button" onClick={reset}>
|
||||
<HiCog />
|
||||
Сброс настроек
|
||||
</button>
|
||||
<button type="button" onClick={setDefaultTheme}>
|
||||
<HiOutlineEyeOff />
|
||||
Обычная версия
|
||||
</button>
|
||||
<button type="button" onClick={play}>
|
||||
<HiPlay />
|
||||
Прослушать всё
|
||||
</button>
|
||||
<VoiceSelect
|
||||
value={p.voice}
|
||||
onChange={event => {
|
||||
setVoice(event.target.value)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccessibilityPanel
|
||||
@@ -1,90 +0,0 @@
|
||||
/*body {
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 96%;
|
||||
padding-top: 90px;
|
||||
}*/
|
||||
|
||||
.viewsettings {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
color: black;
|
||||
background: white;
|
||||
z-index: 10;
|
||||
font-size: 20px !important;
|
||||
line-height: 30px;
|
||||
font-family: sans-serif;
|
||||
letter-spacing: normal !important;
|
||||
|
||||
/*position: fixed;
|
||||
top: 0;*/
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
|
||||
text-align: center;
|
||||
border: 2px solid;
|
||||
}
|
||||
|
||||
.viewsettings .row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.viewsettings p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.viewsettings .block {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
line-height: 30px;
|
||||
border: 2px solid;
|
||||
padding: 6px 10px;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.viewsettings .block button {
|
||||
display: inline-block;
|
||||
border: 1px solid;
|
||||
text-decoration: none;
|
||||
padding: 4px 10px;
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
color: black;
|
||||
font: inherit;
|
||||
}
|
||||
.viewsettings .block button:hover {
|
||||
background-color: #cfcfcf !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.viewsettings .block button:focus {
|
||||
outline: 1px solid !important;
|
||||
}
|
||||
|
||||
.viewsettings button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
border: 1px solid;
|
||||
font-size: 20px !important;
|
||||
padding: 4px 10px;
|
||||
cursor: pointer;
|
||||
|
||||
color: black;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.viewsettings button:hover {
|
||||
background-color: #cfcfcf !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.viewsettings button:focus {
|
||||
outline: 1px solid !important;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
function useVoices() {
|
||||
return window.speechSynthesis.getVoices().map(v => {
|
||||
return {
|
||||
name: `${v.name} (${v.lang})`,
|
||||
value: v.name,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export type VoiceSelectProps = React.SelectHTMLAttributes<HTMLSelectElement>
|
||||
|
||||
function VoiceSelect(props: VoiceSelectProps) {
|
||||
const voices = useVoices()
|
||||
return (
|
||||
<select {...props}>
|
||||
{voices.map(voice => (
|
||||
<option key={voice.value} value={voice.value}>
|
||||
{voice.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)
|
||||
}
|
||||
|
||||
export default VoiceSelect
|
||||
@@ -1,18 +0,0 @@
|
||||
.navigation {
|
||||
padding: var(--size-s);
|
||||
}
|
||||
|
||||
.footerLinks {
|
||||
margin-bottom: var(--size-l);
|
||||
}
|
||||
|
||||
.footerPanel {
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.gosuslugi {
|
||||
background-color: white;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { useMobile } from "@hudozka/hooks"
|
||||
import { Box, Breadcrumbs, Panel, Title } from "@hudozka/ui"
|
||||
import dynamic from "next/dynamic"
|
||||
import Link from "next/link"
|
||||
import { usePathname } from "next/navigation"
|
||||
import { Copyright } from "@/components/Copyright"
|
||||
import { Navigation } from "@/components/Navigation"
|
||||
import { yearRange } from "@/const"
|
||||
import useAccessibility from "@/hooks/useAccessibility"
|
||||
import type { BreadcrumbPart } from "@/types"
|
||||
import { AccessibilityButton } from "../AccessibilityButton"
|
||||
import AccessibilityPanel from "../AccessibilityPanel"
|
||||
import { Author } from "../Author"
|
||||
import { Contacts } from "../Contacts"
|
||||
import { Content } from "../Content"
|
||||
import { Footer } from "../Footer"
|
||||
import { Spacer } from "../Spacer"
|
||||
import { Wrapper } from "../Wrapper"
|
||||
import s from "./app.module.css"
|
||||
|
||||
const Gosuslugi = dynamic(() => import("../Gosuslugi").then(module => ({ default: module.Gosuslugi })), {
|
||||
ssr: false,
|
||||
})
|
||||
|
||||
export type AppProps = {
|
||||
showAuthor?: boolean
|
||||
contentStyle?: React.CSSProperties
|
||||
breadcrumbs?: BreadcrumbPart[]
|
||||
children?: React.ReactNode
|
||||
}
|
||||
|
||||
export function App({ showAuthor = false, ...props }: AppProps) {
|
||||
const hideBreadcrumbs = useMobile()
|
||||
const accessibility = useAccessibility()
|
||||
const pathname = usePathname()
|
||||
|
||||
return (
|
||||
<Wrapper
|
||||
header={
|
||||
<header>
|
||||
{!accessibility ? null : <AccessibilityPanel />}
|
||||
|
||||
<div className={s.navigation}>
|
||||
<Navigation />
|
||||
</div>
|
||||
{hideBreadcrumbs || !props.breadcrumbs ? null : (
|
||||
<Box center>
|
||||
<Content>
|
||||
<Breadcrumbs items={props.breadcrumbs} path={pathname} />
|
||||
</Content>
|
||||
</Box>
|
||||
)}
|
||||
</header>
|
||||
}
|
||||
footer={
|
||||
<Footer>
|
||||
<Box wrap gap={40} align={false} className={s.footerLinks}>
|
||||
<Panel ghost>
|
||||
<Contacts />
|
||||
</Panel>
|
||||
|
||||
<Panel ghost className={s.footerPanel}>
|
||||
<Title level={3}>Версия для слабовидящих</Title>
|
||||
<AccessibilityButton />
|
||||
</Panel>
|
||||
|
||||
<Panel ghost className={s.footerPanel}>
|
||||
<Title level={3}>Анкеты</Title>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<Link href={"https://bus.gov.ru/qrcode/rate/351942"}>
|
||||
Независимая оценка качества оказания услуг
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<Link href={"https://forms.mkrf.ru/e/2579/xTPLeBU7/?ap_orgcode=600340071"}>
|
||||
Анкета Министерства культуры РФ
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<Link href={"https://forms.yandex.ru/u/68ef8e9c90fa7b0bacf46e9d/"}>
|
||||
Анкета получателей образовательных услуг ДОП Кировского муниципального района
|
||||
Ленинградской области 2025
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</Panel>
|
||||
</Box>
|
||||
|
||||
<div className={s.gosuslugi}>
|
||||
<Gosuslugi />
|
||||
</div>
|
||||
|
||||
<Box>
|
||||
<Copyright years={yearRange} />
|
||||
|
||||
{!showAuthor ? null : (
|
||||
<>
|
||||
<Spacer as={"span"} />
|
||||
<Author />
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Footer>
|
||||
}
|
||||
>
|
||||
<Box center>
|
||||
<Content style={props.contentStyle}>{props.children}</Content>
|
||||
</Box>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export const Author: React.FC = () => (
|
||||
<span>
|
||||
Разработка и поддержка —{" "}
|
||||
<a href="http://tmshv.ru?utm_source=artshburg&utm_medium=developer&utm_campaign=tmshv_ru">www.tmshv.ru</a>
|
||||
</span>
|
||||
)
|
||||
@@ -1,22 +0,0 @@
|
||||
import cx from "classnames"
|
||||
import Link from "next/link"
|
||||
import s from "./styles.module.css"
|
||||
|
||||
export type CardLayout = "simple" | "featured"
|
||||
export type CardProps = {
|
||||
children?: React.ReactNode
|
||||
style?: React.CSSProperties
|
||||
href: string
|
||||
cover: React.ReactNode
|
||||
layout: CardLayout
|
||||
}
|
||||
|
||||
export function Card(props: CardProps) {
|
||||
return (
|
||||
<Link href={props.href} className={cx(s.card, s.simple)} style={props.style}>
|
||||
<div className={s.image}>{props.cover}</div>
|
||||
|
||||
<div className={s.body}>{props.children}</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/* CARD */
|
||||
|
||||
.card {
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
|
||||
color: var(--color-text);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.card,
|
||||
a.card:hover {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.image {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
.image img {
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
.body {
|
||||
background-color: var(--color-back);
|
||||
padding: var(--size-m);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
/* CARD ANIMATION */
|
||||
|
||||
/* .card:hover { */
|
||||
/* box-shadow: var(--shadow-active); */
|
||||
/* } */
|
||||
|
||||
/* IMAGE ANIMATION */
|
||||
|
||||
.card .image {
|
||||
transition: box-shadow 250ms ease, transform 250ms ease;
|
||||
}
|
||||
|
||||
.card:hover .image {
|
||||
box-shadow: var(--shadow-active);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* SIMPLE */
|
||||
|
||||
.card.simple {
|
||||
background-color: var(--color-back);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* FEATURED */
|
||||
|
||||
.card.featured {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.featured .body {
|
||||
position: absolute;
|
||||
|
||||
--offset: var(--size-m);
|
||||
left: var(--offset);
|
||||
right: var(--offset);
|
||||
bottom: var(--offset);
|
||||
|
||||
border-radius: var(--radius-small);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import s from "./styles.module.css"
|
||||
|
||||
export type CardGridProps = {
|
||||
children?: React.ReactNode
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
export const CardGrid: React.FC<CardGridProps> = ({ children, style }) => {
|
||||
return (
|
||||
<section className={s.grid} style={style}>
|
||||
{children}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
.grid {
|
||||
--min-cell-size: 270px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(var(--min-cell-size), 1fr));
|
||||
display: grid;
|
||||
grid-auto-rows: 25em;
|
||||
grid-auto-flow: dense;
|
||||
grid-gap: var(--size-l) var(--size-l);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 31.25em) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
grid-gap: var(--size-m);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
.strong {
|
||||
margin-bottom: var(--size-m);
|
||||
}
|
||||
|
||||
.link {
|
||||
margin-bottom: var(--size-s);
|
||||
}
|
||||