Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Symbol Web · Symbol Studio

Превратите обычный текст в рисунок из символов. Например, Hello становится большой надписью, которую можно вставить в README, сообщение или текстовый файл. Здесь нет картинок: каждая линия состоит из букв, пробелов и знаков.

Запуск готовой версии: go run ./cmd/web из корня репозитория. Затем откройте http://127.0.0.1:8080. Без настройки AI всё работает в демо-режиме, без ключа и внешней сети.

Исходная реализация команды в cmd/server, исходные шаблоны и исходные алгоритмы сохранены без изменений. Готовая версия подключается отдельной точкой входа cmd/web и дополняет те же пакеты. Команда go run cmd/server/main.go запускает первоначальный сервер, в котором AI-обработчики ещё пустые. Для проверки доработанной версии используйте команду выше.

Description · Что умеет проект

  • Генерирует ASCII-арт в трёх стилях: Standard, Shadow, Thinkertoy.
  • Поддерживает многострочный ввод, CRLF и запись \n; ограничение — 1000 символов.
  • Принимает печатные ASCII-символы: латиницу, цифры, пробелы и знаки. Кириллица, emoji, табуляция и управляющие символы не входят в комплект шрифтов и получают понятную ошибку.
  • Предлагает 3–5 продолжений текста после паузы в наборе.
  • Подбирает стиль по объяснимым правилам без обращения к AI.
  • Предлагает 3–5 творческих вариантов с готовым стилем.
  • Позволяет копировать результат, скачивать .txt и повторять последние восемь надписей.
  • Работает на узком экране, с клавиатуры и без JavaScript: без JS остаются обычная HTML-форма и генерация.

Технологии: Go и его стандартная библиотека (net/http, html/template, encoding/json), обычные HTML, CSS и JavaScript. Нет frontend-сборки, npm-зависимостей, CDN, базы данных или обязательного облачного сервиса.

Authors · Участники

Участник Вклад
asamat Исходная серверная структура, форма, стили, генерация ASCII и заготовка AIClient; вклад виден в истории Git.
abaitas Участник исходной команды по информации владельца проекта. В полученной истории нет отдельного автора с этим логином, поэтому конкретные коммиты ему не приписаны.
ysoltamb Интеграция надстроек: безопасная генерация, HTTP-обработчики, AI-сервис, интерфейс Studio, проверки и документация.

История и авторство прежних коммитов сохранены. Все 22 прежних файла, кроме изначально пустого README.md, сохранены побайтно относительно 76b2ff96c1ded7eaa04df02fec4c457a0b09d0f5. Проверка: python3 scripts/verify_preserved.py.

Usage · Как запустить

Если вы впервые запускаете программу

  1. Установите Go 1.27.1 или новее — эта версия указана в исходном go.mod. Go нужен, чтобы собрать и запустить сервер.

  2. Скачайте репозиторий и откройте терминал в его папке:

    git clone https://01.tomorrow-school.ai/git/asamat/symbol-web.git
    cd symbol-web
  3. Запустите:

    go run ./cmd/web
  4. Откройте в браузере http://127.0.0.1:8080.

  5. Введите Hello, world!, выберите стиль и нажмите «Создать ASCII-арт».

  6. Чтобы остановить сервер, нажмите Ctrl+C в терминале.

Если в окружении уже задан адрес LLM, для гарантированного демо-запуска:

LLM_BASE_URL= LLM_MODEL= go run ./cmd/web

На Windows PowerShell:

$env:LLM_BASE_URL = ""
$env:LLM_MODEL = ""
go run ./cmd/web

Конфигурация

Переменная Если не задана Назначение
LLM_BASE_URL Пусто → mock Адрес OpenAI-совместимого сервиса. Для Ollama: http://127.0.0.1:11434.
LLM_MODEL Пусто Имя установленной модели. Обязательно при непустом адресе.
LLM_API_KEY Пусто Необязательный ключ для сервиса с Bearer-авторизацией. Ollama локально обычно не требует ключа.
ADDR 127.0.0.1:8080 Адрес и порт самого приложения.
APP_ROOT . Папка с templates, static и тремя файлами баннеров.

Исходный .env.example сохранён. Добавленный .env.studio.example показывает все параметры и безопасный демо-запуск. Go не читает .env автоматически. В POSIX-оболочке можно загрузить переменные явно:

cp .env.studio.example .env
set -a
. ./.env
set +a
go run ./cmd/web

Для реального LLM установите и запустите выбранный совместимый сервер, подготовьте в нём модель и задайте её реальное имя:

export LLM_BASE_URL="http://127.0.0.1:11434"
export LLM_MODEL="имя-установленной-модели"
go run ./cmd/web

При адресе без пути приложение использует /v1/chat/completions. При адресе с путём, например https://ваш-сервис/v1, добавляется /chat/completions. Передавайте базовый URL, а не полный адрес метода. URL с логином/паролем, query-параметрами или fragment отклоняется. Модель и адрес не зашиты в код.

В live-режиме текст подсказки передаётся выбранному сервису; интерфейс сообщает об этом. Ключ передаётся только сервером и не попадает в HTML, JavaScript, историю браузера или сообщения об ошибках. При недоступной LLM генератор и локальный подбор стиля продолжают работать. Неудачный live-запрос возвращает ошибку, а не маскируется демо-ответом.

Сборка и другие порты

go build -o bin/symbol-web ./cmd/web
./bin/symbol-web
ADDR=127.0.0.1:8090 go run ./cmd/web

Запускайте из корня проекта либо задавайте абсолютный APP_ROOT. Программа читает ресурсы из файлов: бинарник нужно распространять вместе с ними. Для доступа из контейнера или другой машины можно явно задать ADDR=:8080. Для публичного размещения нужны отдельно настроенные TLS, ограничения трафика и инфраструктура; готовый сервер по умолчанию доступен только локально.

Implementation Details · Как всё устроено

Слои проекта

cmd/
  server/main.go                исходная точка входа, сохранена
  web/main.go                   точка входа готовой надстройки
internal/
  ascii/
    generator.go               исходный код, сохранён
    safe_renderer.go           проверяемое чтение и локальная сборка строк
    safe_renderer_test.go      примеры аудита, границы, параллельность, фаззинг
    testdata/                  эталоны из выданного аудита
  ai/
    client.go                  исходный AIClient, используется надстройкой
    rules.go                   детерминированный подбор стиля
    service.go                 mock/live, таймаут, проверка ответа, кэш
    service_test.go            реальный тестовый HTTP-backend и проверки отказов
  handlers/
    ascii.go                   исходная реализация, сохранена
    web.go                     HTTP-надстройка и связывание пакетов
    web_test.go                маршруты, ошибки, XSS, конкуренция и фаззинг
  ...                          исходные файлы остаются на местах
templates/index.html          интерфейс Studio и результат генерации
templates/studio-error.html   страница ошибки
static/css/studio.css         адаптивное оформление
static/js/studio.js           асинхронные действия и локальная история
standard.txt / shadow.txt / thinkertoy.txt
.env.example / .env.studio.example
Makefile
scripts/verify_preserved.py
AUDIT_ANSWERS.md
AUDIT_REPORT.md

Надстройка сохраняет цепочку cmd → handlers → ascii/ai. Она не вызывает небезопасные для HTTP-запросов старые Generate/Banner: у них общая переменная и завершение процесса при ошибке. Вместо этого расширяет те же пакеты отдельными функциями и использует общую исходную формулу initPositions. Нет перезаписи чужого кода, monkey patching или скрытого запуска через init().

Алгоритм ASCII

Файл баннера содержит 95 символов ASCII, от кода 32 до 126. На каждый приходится пустой разделитель и восемь строк рисунка. Позиция первой строки вычисляется исходной функцией initPositions. После проверки файла генератор идёт по строкам текста, затем по восьми строкам высоты и склеивает соответствующие части букв через локальный strings.Builder.

Ввод проверяется до обращения к файлам. Выбор баннера ограничен тремя именами; путь пользователя не открывается. Файл ограничен 128 KiB, строка рисунка — 64 байтами; проверяются размер таблицы, разделители и печатные символы. Каждый запрос имеет собственный буфер. Пустые строки сохраняются; ввод только из переводов строк возвращает такое же число переводов. Пробелы не обрезаются. Ввод \n намеренно интерпретируется как перенос, как в исходном проекте.

HTTP API

Метод и путь Запрос Успешный ответ
GET / — HTML-страница с формой
HEAD / — Заголовки страницы без тела
POST /symbol-art URL-encoded поля inputWord, banners HTML с результатом; при Accept: application/json — {art,text,banner}
POST /api/suggest JSON {text}; минимум 3 символа после обрезки внешних пробелов {suggestions: [строки]}
POST /api/recommend-banner JSON {text} {recommended, reasoning, alternatives}
POST /api/variations JSON {text} {variations: [{text, description, suggested_banner}]}
GET /static/css/studio.css — CSS
GET /static/js/studio.js — JavaScript

Форма принимает как standard, так и исходное standard.txt (аналогично остальным стилям). API принимает ровно один JSON-объект с полем text. Неизвестные поля и лишние данные отклоняются. Максимальное тело запроса — 16 KiB. AI-ответы всегда JSON, включая ошибки. X-AI-Mode: mock или live показывает режим LLM-методов.

curl -i http://127.0.0.1:8080/

curl -i http://127.0.0.1:8080/symbol-art \
  --data-urlencode 'inputWord=Hello' --data-urlencode 'banners=standard'

curl -s http://127.0.0.1:8080/symbol-art \
  -H 'Accept: application/json' \
  --data-urlencode 'inputWord=Hello' --data-urlencode 'banners=shadow'

curl -s http://127.0.0.1:8080/api/suggest \
  -H 'Content-Type: application/json' -d '{"text":"Happy Birth"}'

curl -s http://127.0.0.1:8080/api/recommend-banner \
  -H 'Content-Type: application/json' -d '{"text":"WELCOME"}'

curl -s http://127.0.0.1:8080/api/variations \
  -H 'Content-Type: application/json' -d '{"text":"Hello"}'

Ошибки и устойчивость

Код Пример
200 Успешная страница, генерация или AI-ответ
400 Пустой/слишком длинный текст, неподдерживаемый символ, неверный баннер, повреждённый JSON
404 Неизвестный маршрут, отсутствующий шаблон или файл баннера
405 Неподходящий HTTP-метод; ответ содержит Allow
415 Неверный Content-Type
500 Повреждённый ресурс, ошибка шаблона, неправильная конфигурация/ответ LLM
503 Сеть, таймаут, upstream 429/5xx или заняты все восемь LLM-слотов

API-ошибка имеет вид {"error":"понятное сообщение","status":400}. Внутренние пути и ответы провайдера не отправляются в браузер. Шаблон сначала полностью выполняется в буфер, поэтому ошибка не оставляет страницу с ошибочным статусом 200. Пропавший шаблон ошибки имеет резервный текстовый ответ. Защитные заголовки запрещают внешние скрипты, фреймы и выполнение HTML из пользовательского текста. Содержимое выводится через html/template и textContent.

У сервера есть ограничения времени чтения заголовков/тела, записи и простоя. Ctrl+C/SIGTERM отменяет контекст исходящих вызовов и завершает сервер с ограниченным ожиданием. Программная ошибка обработчика перехватывается; это последний резерв, а не замена обычной обработки ошибок.

Frontend

Один скрипт без зависимостей использует fetch, AbortController, DOM API и обычные формы. Подсказки ждут 300 ms после ввода, не запрашиваются для текста короче трёх символов и отменяются при новом вводе. Устаревшие ответы не заменяют актуальный ввод. Кнопки показывают состояние ожидания и восстанавливаются после ошибки. ASCII сохраняет пробелы и прокручивается внутри результата, не растягивая страницу.

История хранит только текст и стиль, максимум восемь записей в localStorage. При запрете или переполнении хранилища работает память текущей страницы. Она не сохраняется на сервере. Очистка затрагивает только историю этого приложения. Если браузер запрещает буфер обмена, можно скачать файл или выделить результат вручную.

AI Features · Подсказки, правила и варианты

Mock: при пустом или пробельном LLM_BASE_URL возвращаются предсказуемые примеры. Для Hello: Hello!, Hello World, ~ Hello ~. Они не выдаются за ответы настоящей модели. Длинный ввод для демо-подсказки сокращается до 38 символов, чтобы готовые варианты были короче 50.

Live: используется исходный AIClient с таймаутом 10 секунд. Запрос наследует контекст входящего HTTP-запроса и дополнительный срок 10 секунд. Приложение отправляет непотоковый OpenAI-совместимый запрос. Модель получает инструкцию вернуть JSON; после ответа проверяются конверт, количество, длина, ASCII, уникальность и допустимость стилей. Размер ответа ограничен 64 KiB. Редиректы не выполняются. Ошибочные ответы не кэшируются.

Подбор стиля: сначала обрезаются внешние пробелы. Если есть буквы и все они заглавные или длина не превышает 6 — shadow; иначе при длине больше 12 — standard; иначе — thinkertoy. Порядок правил существенен: длинная надпись заглавными тоже получает shadow. Цифры не считаются заглавными буквами. Вместе с рекомендацией возвращаются две альтернативы и пояснения. Значения score — фиксированные относительные оценки, не вероятности модели.

Кэш: успешные подсказки хранятся 5 минут, максимум 256 ключей. Доступ защищён mutex; массивы копируются при выдаче. По истечении срока вызывается backend заново. Варианты не кэшируются. Одновременно выполняется не более восьми внешних вызовов на процесс; лишние получают 503, без неограниченной очереди. Автоматического retry нет: пользователь может повторить запрос, а остальные функции продолжают работать.

Проверка проекта

Для основных Go-проверок нужен Go. Для проверки синтаксиса JS — Node.js, для проверки сохранности файлов — Python 3. В рабочем приложении Node/Python не нужны. -race требует поддерживаемую платформу и системный C-компилятор.

go test -race -timeout 90s ./...
go vet ./...
node --check static/js/studio.js
python3 scripts/verify_preserved.py

Или make audit — vet, синтаксис JS, формат новых Go-файлов, тесты с race detector и сборка.

Дополнительно:

make fuzz
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out

Тесты не требуют внешней LLM, ключа или интернета: live-протокол проверяется локальными httptest-серверами, включая настоящий десятисекундный таймаут. Требуется разрешение окружения открывать loopback-порты.

Подробный протокол: AUDIT_REPORT.md. Объяснения, вопросы и демонстрационные команды: AUDIT_ANSWERS.md. Покрытие старого кода и main() не следует путать с покрытием новой HTTP-цепочки: они отражаются в общей цифре отдельно.

Если что-то не работает

  • Порт занят: остановите прежний процесс или задайте другой ADDR.
  • 404 для ресурса: запускайте из корня либо укажите правильный APP_ROOT; проверьте наличие баннеров и новых шаблонов.
  • 503 для подсказок: проверьте доступность LLM и нагрузку; генерация продолжает работать. Для демо очистите LLM_BASE_URL и перезапустите сервер.
  • 500 для LLM: проверьте модель, базовый URL и формат ответа совместимого сервера; подробность находится в серверном журнале.
  • Русский текст не рисуется: комплект баннеров поддерживает только ASCII. Русский язык интерфейса не расширяет алфавит шрифтов.
  • Результат «съехал» после вставки: используйте моноширинный шрифт или Markdown-блок кода; не удаляйте пробелы.
  • Старый интерфейс или пустые AI-ответы: убедитесь, что запущен cmd/web, а не сохранённый cmd/server.

Проверки подтверждают перечисленные сценарии. Они не заменяют испытания конкретной LLM, хостинга или всех существующих браузеров и не означают абсолютной безотказности.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages