diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..482d520 --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,166 @@ +name: Build +on: + push: + branches: [main, master] + tags: ["v*"] + paths: + - "landing/**" + - "app/**" + - "crates/**" + - "Cargo.toml" + - "Cargo.lock" + - ".gitea/workflows/build.yaml" + +env: + REGISTRY: git.realmanual.ru + IMAGE_PREFIX: ${{ gitea.repository }} + +permissions: + contents: read + packages: write + +jobs: + # --------------------------------------------------------------------------- + # Decide what changed so we don't rebuild the (slow) DMG on a landing-only edit + # and vice versa. Tags always build everything (release). + # --------------------------------------------------------------------------- + changes: + name: Detect changes + runs-on: ubuntu-22.04 + container: catthehacker/ubuntu:act-latest + outputs: + landing: ${{ steps.filter.outputs.landing }} + app: ${{ steps.filter.outputs.app }} + steps: + - uses: actions/checkout@v3 + with: { fetch-depth: 2 } + - id: filter + uses: dorny/paths-filter@v3 + with: + filters: | + landing: + - 'landing/**' + app: + - 'app/**' + - 'crates/**' + - 'Cargo.toml' + - 'Cargo.lock' + + # --------------------------------------------------------------------------- + # Landing → static nginx image pushed to the Gitea registry. + # --------------------------------------------------------------------------- + landing: + name: Build & push landing + needs: changes + if: needs.changes.outputs.landing == 'true' || startsWith(github.ref, 'refs/tags/') + runs-on: ubuntu-22.04 + container: catthehacker/ubuntu:act-latest + outputs: + version: ${{ steps.version.outputs.VERSION }} + status: ${{ steps.build.outcome }} + steps: + - uses: actions/checkout@v3 + - name: Read version + id: version + run: echo "VERSION=$(cat ./landing/VERSION)" >> $GITHUB_OUTPUT + - name: Log in to Gitea Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.TOKEN }} + - name: Build and push + id: build + uses: docker/build-push-action@v6 + with: + context: ./landing + file: ./landing/Dockerfile + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/spacesh-landing:${{ steps.version.outputs.VERSION }} + ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/spacesh-landing:latest + + # --------------------------------------------------------------------------- + # macOS app → universal (Intel + Apple Silicon) .dmg. + # REQUIRES a self-hosted macOS runner labelled `macos` — Tauri cannot + # cross-compile a macOS bundle from Linux. The DMG is UNSIGNED (no Developer + # ID secrets configured); Gatekeeper will warn on first open. To sign+notarize + # later, set APPLE_CERTIFICATE / APPLE_SIGNING_IDENTITY / APPLE_ID secrets and + # pass them through to `tauri build`. + # --------------------------------------------------------------------------- + dmg: + name: Build macOS DMG + needs: changes + if: needs.changes.outputs.app == 'true' || startsWith(github.ref, 'refs/tags/') + runs-on: macos + outputs: + version: ${{ steps.version.outputs.VERSION }} + status: ${{ steps.build.outcome }} + steps: + - uses: actions/checkout@v3 + - name: Read version + id: version + run: echo "VERSION=$(node -p "require('./app/src-tauri/tauri.conf.json').version")" >> $GITHUB_OUTPUT + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Install Rust + macOS targets + run: | + if ! command -v rustup >/dev/null; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + echo "$HOME/.cargo/bin" >> $GITHUB_PATH + export PATH="$HOME/.cargo/bin:$PATH" + fi + rustup target add aarch64-apple-darwin x86_64-apple-darwin + - name: Install frontend deps + working-directory: app + run: npm ci + - name: Build universal DMG + id: build + working-directory: app + run: npm run tauri build -- --target universal-apple-darwin + - name: Collect DMG + run: | + set -euo pipefail + mkdir -p dist + cp app/src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg dist/ + ls -lh dist + - name: Upload DMG artifact + uses: actions/upload-artifact@v3 + with: + name: spacesh-dmg-${{ steps.version.outputs.VERSION }} + path: dist/*.dmg + + # --------------------------------------------------------------------------- + # Summary → Max. + # --------------------------------------------------------------------------- + notify: + name: Notify Max + needs: [landing, dmg] + if: always() + runs-on: ubuntu-22.04 + container: catthehacker/ubuntu:act-latest + steps: + - name: Compose & send summary + run: | + line_for() { + local name="$1" result="$2" ver="$3" + case "$result" in + success) echo "✅ $name собран (\`$ver\`)";; + failure) echo "❌ $name — ошибка сборки";; + skipped) echo "➖ $name без изменений";; + *) echo "❔ $name — $result";; + esac + } + summary="" + summary="$summary"$'\n'"$(line_for spacesh-landing '${{ needs.landing.result }}' '${{ needs.landing.outputs.version }}')" + summary="$summary"$'\n'"$(line_for spacesh-dmg '${{ needs.dmg.result }}' '${{ needs.dmg.outputs.version }}')" + + url="${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_number }}" + text=$(printf '**Build**%s\n\n[лог](%s)' "$summary" "$url") + + curl -s -X POST "https://platform-api.max.ru/messages?chat_id=${{ secrets.MAX_CHAT_ID }}" \ + -H "Authorization: ${{ secrets.MAX_BOT_TOKEN }}" \ + -H "Content-Type: application/json" \ + -d "$(jq -n --arg t "$text" '{text:$t,format:"markdown"}')" diff --git a/DOCS/landing/spaceshell-landing.md b/DOCS/landing/spaceshell-landing.md new file mode 100644 index 0000000..dd49bbc --- /dev/null +++ b/DOCS/landing/spaceshell-landing.md @@ -0,0 +1,183 @@ +# spacesh — лендинг (бриф + текст) + +Домен: **spaceshell.ru** · Язык: русский · Стиль: terminal-dark · CTA: Скачать для macOS + +--- + +## 1. Промпт для разработки + +> Скопировать целиком в AI-билдер (v0 / Lovable / Claude) или дать фронтенд-разработчику. + +``` +Построй одностраничный лендинг для продукта «spacesh» — терминал-воркспейс +для параллельного запуска AI-агентов на macOS. Домен spaceshell.ru. Язык +интерфейса — русский. Стадия — early access (pre-v1), будь честен в формулировках. + +# Стек и поставка +- Astro + Tailwind CSS. Один статический маршрут, деплой на любой статический хост. +- Анимации hero — один React-остров (Astro island) или чистый CSS/Canvas; остальная + страница — zero-JS. Цель Lighthouse: 95+ по всем осям, LCP < 1.5s. +- Семантический HTML, корректная иерархия заголовков, prefers-reduced-motion. + +# Позиционирование (одной фразой) +«spacesh держит живые сессии AI-агентов в фоновом демоне — закрой окно, обнови +приложение, словив краш: агенты продолжают работать». + +# Визуальный язык — terminal-dark +- Фон: #0A0D12 (база), панели #0E1116 / #11161F, границы #232A33 / #323C49. +- Текст: #E6EDF3 (основной), #8B97A6 (вторичный), #5A6573 (приглушённый). +- Акцент: бирюзовый неон #34D3C2 (primary), синий #4F9CF9 (secondary). Статусы: + work #4C8DFF, wait #F2B84B, done #3FB950, error #F4544E. +- Шрифты: JetBrains Mono (заголовки-акценты, код, лейблы, цифры) + Inter (тело). +- Текстуры: едва заметная сетка/scanlines на фоне hero, мягкое свечение (glow) под + акцентными элементами, скруглённые панели radius 8–14 как в самом приложении. +- Курсор-каретка (мигающий блок ▌) как лейтмотив бренда; иконка приложения — + тёмная плитка с промптом «>_» бирюзой (есть в app/src-tauri/icons/icon.svg). + +# Структура секций (сверху вниз) +1. Хедер: лого «spacesh» (mono) + nav (Возможности · Как работает · CLI · GitHub) + + кнопка «Скачать для macOS». Sticky, прозрачный → размытие при скролле. +2. Hero: + - Eyebrow: «Терминал-воркспейс для AI-агентов · macOS». + - H1 (крупно, mono-акцент в части слова): см. текст ниже. + - Подзаголовок, две кнопки: primary «Скачать для macOS», secondary «Как это работает». + - Микро-строка под кнопками: «macOS 13+ · Apple Silicon и Intel · открытый исходник». + - Справа/снизу — АНИМИРОВАННЫЙ макет приложения: сетка из 3–4 терминал-панелей, + в каждой «агент» (Claude Code, Codex, Gemini, shell) со status-кольцом; в одной + идёт печать вывода (typewriter), кольца переключаются work→done. Ключевой момент + анимации: окно «закрывается» (затемняется), а маленький бейдж «daemon · alive» + продолжает гореть, затем окно возвращается и мгновенно перерисовывается из снапшота. +3. Лента агентов: «Работает с: Claude Code · Codex · Gemini · opencode · shell». +4. Проблема → решение (короткий контраст-блок): «GUI падает — агент умирает вместе с + ним» → ответ spacesh. +5. Сетка возможностей (6 карточек, terminal-dark, с mono-заголовками и иконкой): + демон-источник истины, параллельные агенты в гриде, статусы пушем, гибридный + терминал (поиск/снапшоты), CLI, тема/настройки. Тексты — ниже. +6. «Как это работает» — 3 шага с мини-диаграммой (spawn → daemon владеет PTY → reattach + из снапшота). Подпись про один Unix-сокет и length-prefixed JSON. +7. Полоса «В планах» (roadmap, честно): внешние уведомления Telegram + MAX, + diff-просмотр изменений агента, remote через SSH-туннель. +8. Tech-полоса: «Rust · Tauri 2 · tokio · xterm.js · alacritty» + бюджет «< 16 мс на нажатие». +9. Финальный CTA: крупная кнопка «Скачать для macOS» + ссылка «Исходники на GitHub». + Опционально мини-форма email «Сообщить о релизе». +10. Футер: spaceshell.ru, © 2026, ссылки (GitHub, Документация, Лицензия), строка + «Сделано для тех, кто гоняет агентов пачками». + +# Интерактив и анимации +- Hero-терминал: печать вывода через requestAnimationFrame, мигающая каретка, + плавная смена status-колец. Уважать prefers-reduced-motion (показывать статичный кадр). +- Карточки возможностей: лёгкий lift + свечение границы на hover. +- Скролл-ревилы (fade/translate, ≤ 300мс), без тяжёлых либ. + +# SEO / мета (RU) +-
Терминал-воркспейс для AI-агентов · macOS
++ spacesh держит живые сессии Claude Code, Codex, Gemini и shell в фоновом демоне. + Закрыл окно, обновил приложение, словил краш — агенты продолжают работать. +
+ + +Проблема
++ Закрыл вкладку, перезапустил приложение, упал GUI — длинная сессия агента умирает вместе с ним. +
+Решение
++ Сессиями владеет фоновый демон, а не окно. Интерфейс — всего лишь вид поверх него. +
++ spaceshd владеет живыми PTY-сессиями. GUI и CLI — тонкие клиенты поверх одного Unix-сокета. + Убей интерфейс — агент жив. Открой заново — экран восстановится из снапшота за доли секунды. +
++ Несколько агентов в раскладке-гриде: сплиты, зум панели, перетаскивание, пресеты (2×2, 1+2, 2×3…), + воркспейсы и избранное. +
++ work · wait · done · error · idle приходят пушем — от хуков агентов, маркеров OSC 133 и паттернов. + Кольца, бейджи, центр событий и нативные уведомления macOS. +
++ xterm.js рисует, грид alacritty в демоне анализирует. Отсюда — поиск по скроллбэку (⌘F) с подсветкой, + извлечение последней команды и мгновенные снапшоты для reattach. +
++ spacesh status --json, focus, new-surface, notify — те же команды, что и в интерфейсе, плюс shell-completions. + Встраивай spacesh в свои пайплайны. +
++ Тёмная и светлая темы, акцентные цвета, шрифт и размер терминала, дефолтный shell — + всё хранится демоном в config.toml и применяется на лету ко всем окнам. +
++ Создаёшь воркспейс и панели — демон спавнит PTY-сессии под агентов. +
++ Байты летают GUI ↔ демон ↔ PTY по одному сокету. Интерфейс состояния не хранит — только команды и события. +
++ Закрыл и открыл приложение — демон отдаёт снапшот экрана, окно перерисовывается мгновенно. +
+В планах
+