refactor: remove pre-Studio website; wire arikigame.com into monorepo
Delete web/landing (legacy tinqs-ltd/website Next.js). Add web/arikigame with static public site and deploy-arikigame.yml (S3 + CloudFront). Link Ariki from tinqs.com logged-out home. Fix build.yml to trigger on main. Co-authored-by: Cursor <cursoragent@cursor.com>
@@ -2,9 +2,9 @@ name: Build tinqs-git
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [tinqs/main]
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [tinqs/main]
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Deploy arikigame.com
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'web/arikigame/**'
|
||||
- '.gitea/workflows/deploy-arikigame.yml'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: host
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Deploy static site to S3 + CloudFront
|
||||
env:
|
||||
S3_BUCKET: arikigame.com
|
||||
CF_DISTRIBUTION: EDMY8TXLTDXLQ
|
||||
run: |
|
||||
set -e
|
||||
SRC="web/arikigame/public"
|
||||
if [ ! -d "$SRC" ]; then
|
||||
echo "Missing $SRC — nothing to deploy"
|
||||
exit 1
|
||||
fi
|
||||
echo "Syncing $SRC → s3://${S3_BUCKET}/"
|
||||
aws s3 sync "$SRC" "s3://${S3_BUCKET}/" --delete \
|
||||
--cache-control "public, max-age=300"
|
||||
echo "Invalidating CloudFront ${CF_DISTRIBUTION}..."
|
||||
aws cloudfront create-invalidation \
|
||||
--distribution-id "${CF_DISTRIBUTION}" \
|
||||
--paths "/*"
|
||||
echo "OK arikigame.com deployed from tinqs/studio/web/arikigame/public"
|
||||
@@ -97,6 +97,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="max-width: 900px; margin: 0 auto 48px; padding: 0 24px; text-align: center;">
|
||||
<p style="color: var(--color-text-light); font-size: 0.95rem; margin-bottom: 12px;">We build our own game on this platform.</p>
|
||||
<a href="https://arikigame.com" style="display: inline-block; padding: 14px 28px; background: #1a1510; border: 1px solid #c9935a55; border-radius: 8px; color: #e0b87a; font-weight: 600; text-decoration: none; font-size: 1rem;">Ariki — Polynesian survival colony sim →</a>
|
||||
</div>
|
||||
|
||||
<div class="tinqs-contact">
|
||||
<h2>Get Early Access</h2>
|
||||
<p>We are onboarding studios one at a time. Tell us about your project.</p>
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# arikigame.com — Ariki game website
|
||||
|
||||
Player-facing marketing site for **Ariki**, the game we build with Tinqs Studio.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **URL** | https://arikigame.com |
|
||||
| **Source** | `tinqs/studio/web/arikigame/` (this folder) |
|
||||
| **Deploy** | Gitea Actions — `.gitea/workflows/deploy-arikigame.yml` on push to `main` |
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
web/arikigame/
|
||||
├── public/ ← static files served at arikigame.com (today)
|
||||
│ └── index.html
|
||||
├── README.md
|
||||
└── (future) Next.js app — replace `public/` when `ozan/arikigame` is merged here
|
||||
```
|
||||
|
||||
## What was removed
|
||||
|
||||
The old **tinqs-ltd/website** Next.js app lived at `web/landing/` before monorepo consolidation. That was the pre-Studio company site — deleted 22 May 2026. Platform marketing is **tinqs.com** (`templates/home.tmpl` in the Gitea fork). Game marketing is **arikigame.com** (this folder).
|
||||
|
||||
## Deploy
|
||||
|
||||
Push changes under `web/arikigame/` to `main`. CI syncs `public/` to S3 and invalidates CloudFront.
|
||||
|
||||
Confirm bucket name in workflow env if deploy fails — see `deploy-arikigame.yml`.
|
||||
@@ -0,0 +1,67 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Ariki — Polynesian Survival Colony Sim</title>
|
||||
<meta name="description" content="Ariki is a Polynesian survival colony sim built by Tinqs using Tinqs Studio — git, LFS, AI agents, and Godot in one workflow.">
|
||||
<link rel="canonical" href="https://arikigame.com/">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://arikigame.com/">
|
||||
<meta property="og:title" content="Ariki — Polynesian Survival Colony Sim">
|
||||
<meta property="og:description" content="Survive, build, and lead your colony across the Pacific. Built with Tinqs Studio.">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||
background: #0a0a0f;
|
||||
color: #e8e4df;
|
||||
line-height: 1.6;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.wrap { max-width: 720px; margin: 0 auto; padding: 80px 24px 120px; text-align: center; }
|
||||
h1 { font-size: 2.8rem; font-weight: 300; letter-spacing: 0.02em; margin-bottom: 8px; }
|
||||
h1 span { color: #c9935a; }
|
||||
.tagline { font-size: 1.15rem; color: #9e9890; margin-bottom: 40px; }
|
||||
.cta {
|
||||
display: inline-block;
|
||||
padding: 14px 32px;
|
||||
background: #c9935a;
|
||||
color: #0a0a0f;
|
||||
text-decoration: none;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
margin: 8px;
|
||||
}
|
||||
.cta.secondary {
|
||||
background: transparent;
|
||||
border: 1px solid #c9935a55;
|
||||
color: #e0b87a;
|
||||
}
|
||||
.cta:hover { background: #e0b87a; color: #0a0a0f; }
|
||||
.cta.secondary:hover { border-color: #c9935a; }
|
||||
.studio {
|
||||
margin-top: 64px;
|
||||
padding-top: 32px;
|
||||
border-top: 1px solid #1e1c24;
|
||||
font-size: 0.9rem;
|
||||
color: #837d76;
|
||||
}
|
||||
.studio a { color: #c9935a; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<h1><span>Ariki</span></h1>
|
||||
<p class="tagline">Polynesian survival colony sim — built with Tinqs Studio</p>
|
||||
<p style="color: #9e9890; margin-bottom: 32px; max-width: 520px; margin-left: auto; margin-right: auto;">
|
||||
Lead your people across the Pacific. Ceremony, mana, and colony life — in development at Tinqs.
|
||||
</p>
|
||||
<a class="cta" href="https://store.steampowered.com/app/0" rel="noopener">Wishlist on Steam</a>
|
||||
<a class="cta secondary" href="https://tinqs.com">Built on Tinqs Studio</a>
|
||||
<p class="studio">
|
||||
Made by <a href="https://tinqs.com">Tinqs</a> — we use our own platform to ship this game.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
After
|
@@ -1,43 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
.env*
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
.vercel
|
||||
@@ -1,76 +0,0 @@
|
||||
# Can's Tasks — Marketing & Steam Store Page
|
||||
|
||||
Owner: Can Gunaslan
|
||||
Created: 2026-04-17
|
||||
Context: Onboarding meeting with Ozan
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Setup Complete (Week of Apr 17)
|
||||
|
||||
- [ ] Verify all tools working: Claude Code, Team Tool, Gitea, AWS CLI
|
||||
- [ ] Install ffmpeg: `winget install Gyan.FFmpeg`
|
||||
- [ ] Set AWS credentials: `aws configure` (keys from Ozan, account 149751500842, region eu-west-1)
|
||||
- [ ] Read this repo's codebase (Next.js static site, deployed to S3+CloudFront at tinqs.com)
|
||||
|
||||
## Phase 2: Research & Prep (Apr 17–21)
|
||||
|
||||
- [ ] Read the GDD v3.1 (`tinqs-ltd/docs/GDD.md`) — understand Ariki's identity, features, positioning
|
||||
- [ ] Study Steam Store page requirements:
|
||||
- Capsule images: Header (460x215), Small (231x87), Main (616x353), Hero (3840x1240), Logo (no fixed size)
|
||||
- Screenshots: minimum 5, recommended 10+ (1920x1080)
|
||||
- Trailer: at least one, MP4, 1080p minimum
|
||||
- Short description: max 300 characters
|
||||
- About the game: detailed HTML description
|
||||
- System requirements: min + recommended specs
|
||||
- Tags/genres: survival, strategy, simulation, multiplayer, early access
|
||||
- [ ] Research comparable Steam pages for tone and layout:
|
||||
- Dawn of Man
|
||||
- Valheim
|
||||
- Going Medieval
|
||||
- Palworld
|
||||
- Farthest Frontier
|
||||
- [ ] Note what works: trailer style, screenshot composition, description structure, tag selection
|
||||
|
||||
## Phase 3: Drafts (Apr 21–25)
|
||||
|
||||
- [ ] Draft short description (300 chars) — Ozan + Ozlem review
|
||||
- [ ] Draft "About This Game" section — HTML formatted, key features, lore hook
|
||||
- [ ] Draft system requirements (check with Ozan for actual specs)
|
||||
- [ ] Create asset checklist for Ozlem (what images/video she needs to produce)
|
||||
- [ ] Identify Early Access specific copy needs (why EA, current state, roadmap, community)
|
||||
|
||||
## Phase 4: Steamworks Setup (with Ozan)
|
||||
|
||||
- [ ] Ozan: create Ariki app in Steamworks
|
||||
- [ ] Ozan: add Can to Steamworks partner group
|
||||
- [ ] Can: enter store page copy into Steamworks
|
||||
- [ ] Ozlem: upload visual assets (capsules, screenshots)
|
||||
- [ ] All: review "Coming Soon" page before publishing
|
||||
- [ ] Publish "Coming Soon" page — start collecting wishlists
|
||||
|
||||
## Phase 5: Marketing Plan (ongoing)
|
||||
|
||||
- [ ] Create marketing timeline (pre-launch, launch, post-launch)
|
||||
- [ ] Identify content creation opportunities (devlogs, trailers, community posts)
|
||||
- [ ] Set up Steam community hub (discussions, announcements)
|
||||
- [ ] Plan social media cadence (X, Discord, YouTube)
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
| Resource | Location |
|
||||
|----------|----------|
|
||||
| GDD v3.1 | `tinqs-ltd/docs/GDD.md` |
|
||||
| Website repo | `tinqs-ltd/website` (this repo) |
|
||||
| Steamworks dashboard | https://partner.steamgames.com |
|
||||
| Steam developer page | https://store.steampowered.com/developer/tinqsstudio |
|
||||
| Bot dashboard | https://bot.arikigame.com |
|
||||
| Indie Marketing Guide | `tinqs-ltd/docs/Indie_Game_Marketing_Guide.md` |
|
||||
|
||||
## Contacts
|
||||
|
||||
- **Ozan** — CTO, technical specs, Steamworks admin
|
||||
- **Ozlem** — CEO/Designer, visual direction, brand approval
|
||||
- **Jeremy** — PM, backlog, coordination
|
||||
@@ -1 +0,0 @@
|
||||
@AGENTS.md
|
||||
@@ -1,120 +0,0 @@
|
||||
# Meeting Log — 2026-04-17 (Ozan + Can)
|
||||
|
||||
**Date:** 2026-04-17, 16:14–18:41 BST
|
||||
**Attendees:** Ozan Bozkurt (CTO), Can Gunaslan, Ozlem (joined briefly near end)
|
||||
**Category:** Meeting — Can onboarding + marketing kickoff
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
### Part 1: Tool Setup (~16:14–17:08)
|
||||
|
||||
Ozan walked Can through the full Tinqs developer toolchain setup on Can's machine (CANG):
|
||||
|
||||
1. **MCP vs CLI explanation** — Ozan explained why the team is moving from MCP (Model Context Protocol) to CLI-based tools. MCP fills up AI context and is expensive. CLI is cheaper, more predictable.
|
||||
|
||||
2. **Tinqs Team Tool demo** — Showed Can:
|
||||
- Screenshot capture (Unity, Chrome, Team Tool itself)
|
||||
- Dual transcription engines (ElevenLabs Scribe + Web Speech) side by side
|
||||
- Live transcript view
|
||||
- Daily cost tracking
|
||||
- S3 cloud upload of transcripts
|
||||
- How agents read transcripts + screenshots to write code and create PRs
|
||||
|
||||
3. **AWS cloud stack** — Briefly discussed. Ozan uses AWS (preference), Melih uses GCP. Each uses what they know.
|
||||
|
||||
4. **Claude Code setup** — Helped Can:
|
||||
- Install Claude Code via npm
|
||||
- Log in to Anthropic Team (can@tinqs.com)
|
||||
- Navigate terminal basics (cd, ls, tab completion)
|
||||
- Open Cursor and connect Claude Code
|
||||
- Session naming (`/rename`, `/color`)
|
||||
|
||||
5. **Setup wizard** (v2.7.0) — Ran the install wizard on Can's machine. Hit issues with:
|
||||
- ffmpeg missing (Team Tool dependency) — **fixed: added to wizard v2.8.0**
|
||||
- AWS CLI winget install failure
|
||||
- Claude Code auth flow with multiple browser tabs open
|
||||
|
||||
6. **Gitea token** — Created Git Studio token for Can's machine (tinqs-team-tool-can-cang)
|
||||
|
||||
7. **Team Tool** — Installed, pinned to taskbar, showed live transcription working
|
||||
|
||||
### Part 2: Marketing Discussion (~17:07–17:10)
|
||||
|
||||
- Ozan mentioned wanting to discuss marketing with Can and Ozlem
|
||||
- Can has availability until 18:00
|
||||
- Can requested a Monday follow-up for topics where he needs Ozan's input
|
||||
- Ozlem was called to join for the marketing conversation
|
||||
- Can mentioned he interviewed a **game designer/PM from Bahcesehir University** for a potential hire (intelligence layer — game jam planning + marketing plan generation)
|
||||
|
||||
### Part 3: Steamworks Setup (~during meeting, Ozan side)
|
||||
|
||||
While chatting with Can, Ozan set up:
|
||||
- Steam account: **tinqsstudio** (ozan)
|
||||
- Steamworks developer registration: **Tinqs Limited**
|
||||
- Fee paid: **£73.99**
|
||||
- Bank details: Monzo USD via WorldPay
|
||||
- W-8BEN-E tax interview started (Corporation, UK)
|
||||
- Website updated with Steam developer page link
|
||||
|
||||
---
|
||||
|
||||
## Action Items
|
||||
|
||||
### Can — Immediate
|
||||
|
||||
- [ ] Complete Claude Code setup (verify `claude --version` works)
|
||||
- [ ] Pin Team Tool to taskbar, verify transcription works
|
||||
- [ ] Save Gitea token securely
|
||||
- [ ] Install ffmpeg (`winget install Gyan.FFmpeg`)
|
||||
- [ ] Set up AWS credentials (`aws configure` — keys from Ozan)
|
||||
|
||||
### Can — Marketing (this week)
|
||||
|
||||
- [ ] Review the `tinqs-ltd/website` repo (Next.js, static site on S3+CloudFront)
|
||||
- [ ] Review the Ariki GDD (v3.1) in `tinqs-ltd/docs/GDD.md`
|
||||
- [ ] Prepare Steam Store page asset checklist (capsule images, screenshots, trailer specs)
|
||||
- [ ] Draft "Coming Soon" store page copy (short description, about section, system requirements)
|
||||
- [ ] Research comparable indie games on Steam for positioning (Dawn of Man, Valheim, Going Medieval)
|
||||
- [ ] Coordinate with Ozlem on visual assets / brand direction
|
||||
|
||||
### Can — Follow-up Monday
|
||||
|
||||
- [ ] Schedule follow-up with Ozan for topics needing his input
|
||||
- [ ] Bring marketing plan outline for discussion
|
||||
|
||||
### Ozan
|
||||
|
||||
- [x] Setup wizard v2.8.0 — added ffmpeg install
|
||||
- [x] Steamworks account registered (tinqsstudio)
|
||||
- [x] Website Steam link updated and pushed
|
||||
- [x] Steamworks setup saved to website + devops repos
|
||||
- [ ] Complete W-8BEN-E tax interview
|
||||
- [ ] Add team members to Steamworks (ozlem, can, jeremy, uygar, melih)
|
||||
- [ ] Create "Ariki" app in Steamworks dashboard
|
||||
|
||||
### Ozlem
|
||||
|
||||
- [ ] Lead Steam Store page visual direction (capsule art, screenshots, trailer)
|
||||
- [ ] Review store copy draft from Can
|
||||
|
||||
---
|
||||
|
||||
## Decisions Made
|
||||
|
||||
1. **Steam account name:** tinqsstudio
|
||||
2. **Payment method:** Monzo USD account via WorldPay
|
||||
3. **Entity type for tax:** Corporation (UK Limited)
|
||||
4. **Can's role:** Marketing first, using Claude Code + Team Tool for coordination
|
||||
5. **Potential hire:** Game designer/PM from Bahcesehir Uni — Can interviewing, may bring on for intelligence layer (game jam + marketing plan generation)
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Can's Windows username is `django` (not `can`) — the setup wizard detected from known users map
|
||||
- Can's machine: CANG
|
||||
- Can is also working on GameCaster (his own company) — will use Claude subscription for both, Ozan is fine with it
|
||||
- Team Tool v4.9.6 requires ffmpeg — was missing from setup wizard, now fixed in v2.8.0
|
||||
- Claude Code auth gets confused with multiple browser tabs — tell new users to close all but one Chrome tab
|
||||
@@ -1,36 +0,0 @@
|
||||
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||
@@ -1,56 +0,0 @@
|
||||
# Steamworks — Tinqs Limited
|
||||
|
||||
Registered: 2026-04-17
|
||||
|
||||
## Account
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Steam username** | ozan |
|
||||
| **Partner name** | tinqsstudio |
|
||||
| **Legal entity** | Tinqs Limited |
|
||||
| **Developer page** | https://store.steampowered.com/developer/tinqsstudio |
|
||||
| **Partner site** | https://partner.steamgames.com |
|
||||
| **Contact email** | ozan@tinqs.com |
|
||||
| **Fee paid** | £73.99 |
|
||||
|
||||
## Payment Details (WorldPay)
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Bank Account Holder** | Tinqs Limited |
|
||||
| **Bank Name** | MONZO |
|
||||
| **Bank SWIFT Code** | TRWIGB2LXXX |
|
||||
| **Sort Code** | 040004 |
|
||||
| **Payee IBAN** | ************9095 |
|
||||
| **Account Type** | Checking |
|
||||
| **Currency** | USD |
|
||||
| **Bank City** | London |
|
||||
| **Bank Postal Code** | EC2A 2AG |
|
||||
| **Bank Country** | United Kingdom |
|
||||
|
||||
## Tax Information
|
||||
|
||||
- Status: **Not yet filed** (W-8BEN-E required for UK company)
|
||||
- Provider: TaxIdentity by Lilaham
|
||||
|
||||
## Team Access
|
||||
|
||||
Add via Users & Permissions on partner.steamgames.com:
|
||||
|
||||
| Email | Role |
|
||||
|-------|------|
|
||||
| ozan@tinqs.com | Owner (admin) |
|
||||
| ozlem@tinqs.com | Owner (admin) |
|
||||
| can@tinqs.com | Developer |
|
||||
| jeremy@tinqs.com | PM |
|
||||
| uygar@tinqs.com | Tester (view only) |
|
||||
| melih@tinqs.com | Consultant (view only) |
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Complete W-8BEN-E tax interview (required before publishing)
|
||||
2. Create "Ariki" app in Steamworks
|
||||
3. Add team members via Users & Permissions
|
||||
4. Prepare store page assets (capsules, screenshots, trailer — Ozlem leads)
|
||||
5. Publish "Coming Soon" page for wishlists
|
||||
@@ -1,140 +0,0 @@
|
||||
---
|
||||
title: "How We Use Cursor with DeepSeek (and Why It's Faster and Cheaper)"
|
||||
date: "2026-05-20"
|
||||
author: "Ozan Bozkurt"
|
||||
excerpt: "We built an OpenAI-compatible proxy that routes Cursor through DeepSeek with caching, backoff, and a shared agent bus. Here's how it works and why our three-person team ships at startup speed."
|
||||
tags: ["cursor", "deepseek", "ai", "devtools", "go", "proxy"]
|
||||
---
|
||||
|
||||
We're a three-person indie game studio building [Ariki](https://arikigame.com) — a Polynesian survival colony sim. We use AI agents heavily: five people on the team, each with their own agent pair, running 8-hour days. Our total spend last week? **$56.**
|
||||
|
||||
This isn't luck. It's a deliberate architecture choice built around a simple idea: **Cursor talks OpenAI, DeepSeek answers OpenAI — and everything in between is a single Go binary a friend can run.**
|
||||
|
||||
---
|
||||
|
||||
## The Problem
|
||||
|
||||
Cursor's agent mode is powerful, but default provider pricing adds up fast with heavy usage. Our team runs multiple concurrent agent sessions daily — coding, reviewing, testing, planning. At standard rates, that would easily hit hundreds per month.
|
||||
|
||||
We wanted:
|
||||
|
||||
- **Cheap inference** — DeepSeek's pricing is drastically lower for comparable quality
|
||||
- **Caching** — Cursor strips `reasoning_content` on replay, so we cache it ourselves
|
||||
- **Queue resilience** — DeepSeek rate-limits aggressively; we needed exponential backoff with a job queue
|
||||
- **Team bus** — multiple agents on one machine should share context (voice transcripts, decisions, traces)
|
||||
- **No vendor lock-in** — swap the backend provider without changing anything in Cursor
|
||||
|
||||
---
|
||||
|
||||
## The Solution: A Local OpenAI-Compatible Proxy
|
||||
|
||||
We wrote a single Go binary (~11 MB) that sits between Cursor and DeepSeek:
|
||||
|
||||
```
|
||||
Cursor IDE ──POST /v1/chat/completions──▶ localhost:8787 ──▶ api.deepseek.com
|
||||
(OpenAI format) (our proxy) (DeepSeek API)
|
||||
```
|
||||
|
||||
Cursor thinks it's talking to OpenAI. Our proxy translates, queues, caches, and forwards. From Cursor's perspective, nothing changes — you point it at `http://127.0.0.1:8787/v1` and keep working.
|
||||
|
||||
### What the proxy does
|
||||
|
||||
**1. Accepts OpenAI-compatible chat completions** — the exact format Cursor sends. No middleware, no adapters. Drop-in.
|
||||
|
||||
**2. Forwards to DeepSeek with exponential backoff** — when DeepSeek returns 429 (rate limit) or 503 (overloaded), the proxy retries with increasing delays. Agents never see the error.
|
||||
|
||||
**3. Caches reasoning content** — DeepSeek sends a `reasoning_content` field alongside the final response. Cursor strips this on replay (the "reasoning" section disappears when you scroll back). We cache it separately so agents can reference previous reasoning chains.
|
||||
|
||||
**4. System prompt hot-reload** — the agent's system prompt ("You are a senior software engineer working inside Cursor IDE...") lives in a file, reloadable without restarting.
|
||||
|
||||
**5. Job queue with Redis** — multiple agents hitting DeepSeek simultaneously? The queue serializes requests, applies backoff globally, and broadcasts results via SSE to all waiting clients.
|
||||
|
||||
**6. Shared agent bus** — the same localhost server that proxies inference also hosts our Team Tool: voice transcription, screenshot capture, trace search, and session management. Every agent on the machine reads the same state.
|
||||
|
||||
---
|
||||
|
||||
## The Numbers
|
||||
|
||||
Running DeepSeek V4 Pro through our proxy:
|
||||
|
||||
| Item | Cost |
|
||||
|------|------|
|
||||
| 5 team members × 2 agents | 10 concurrent sessions |
|
||||
| 8 hours/day, 7 days | 56 agent-hours/week |
|
||||
| **Total spend (1 week)** | **$56** |
|
||||
|
||||
Compare that to default provider pricing for the same workload — we're saving roughly 80-90%.
|
||||
|
||||
---
|
||||
|
||||
## How to Set It Up (For Your Team)
|
||||
|
||||
We're packaging this as an open-source Go binary. Until the repo is public, here's the pattern:
|
||||
|
||||
### 1. Build the proxy
|
||||
|
||||
```bash
|
||||
git clone https://git.arikigame.com/tinqs-ltd/cursor-harness.git
|
||||
cd cursor-harness/cmd/proxy
|
||||
go build -o cursor-proxy .
|
||||
```
|
||||
|
||||
### 2. Configure
|
||||
|
||||
```bash
|
||||
export DEEPSEEK_API_KEY="sk-your-key-here"
|
||||
export LISTEN=":8787"
|
||||
./cursor-proxy
|
||||
```
|
||||
|
||||
### 3. Point Cursor at it
|
||||
|
||||
In Cursor settings, set **OpenAI Base URL** to:
|
||||
|
||||
```
|
||||
http://127.0.0.1:8787/v1
|
||||
```
|
||||
|
||||
That's it. Cursor now routes through DeepSeek.
|
||||
|
||||
### 4. Optional: Redis for multi-agent queueing
|
||||
|
||||
```bash
|
||||
export REDIS_URL="redis://localhost:6379"
|
||||
export CACHE_BACKEND="redis"
|
||||
```
|
||||
|
||||
Without Redis, the proxy uses in-memory caching and direct forwarding — fine for a single user.
|
||||
|
||||
---
|
||||
|
||||
## What We Learned
|
||||
|
||||
**Go is the right language for this.** A single static binary with zero runtime dependencies. Friends on Windows, Mac, or Linux run the same binary. No Node.js, no Python venv, no Docker required (unless you want Redis).
|
||||
|
||||
**Caching reasoning content matters more than you'd think.** DeepSeek's reasoning chains are the most valuable part of the output — they show _why_ the model made a decision. Without caching, Cursor's replay strips them and you lose institutional knowledge.
|
||||
|
||||
**The agent bus is the real multiplier.** Having every agent on the machine read the same voice transcripts, screenshots, and traces means you never ask "what did you want?" — the agents already know what you just said.
|
||||
|
||||
**Queueing prevents thundering herd.** When five agents all fire requests at once, the queue absorbs the spikes, applies backoff once (not five times), and broadcasts results. Dramatically fewer 429s.
|
||||
|
||||
---
|
||||
|
||||
## What's Next
|
||||
|
||||
We're extracting the proxy and agent bus into a proper open-source package (`cursor-harness`). Friends will be able to:
|
||||
|
||||
- Run the proxy as a single binary (no Git Studio account needed)
|
||||
- Plug in any OpenAI-compatible backend (DeepSeek, Groq, Together, local Ollama)
|
||||
- Add the Team Tool bus for shared agent context
|
||||
- Contribute back — MIT license, public repo on Git Studio
|
||||
|
||||
The goal isn't to sell anything. It's to share what we built because we think three-person teams should be able to ship at startup speed without startup burn.
|
||||
|
||||
---
|
||||
|
||||
**Questions?** Find us at [arikigame.com](https://arikigame.com) or on the repo at [git.arikigame.com/tinqs-ltd/cursor-harness](https://git.arikigame.com/tinqs-ltd/cursor-harness).
|
||||
|
||||
---
|
||||
|
||||
*Ozan Bozkurt is CTO at Tinqs, building Ariki — a Polynesian survival colony sim. He writes about AI tooling, game architecture, and shipping fast with small teams.*
|
||||
@@ -1,141 +0,0 @@
|
||||
---
|
||||
title: "Why We Forked Gitea — Large File Storage for Game Dev"
|
||||
date: "2026-05-20"
|
||||
author: "Ozan Bozkurt"
|
||||
excerpt: "Game repos aren't like web repos. A single art asset can be 500MB. GitHub doesn't cut it. Here's why we forked Gitea, added LFS on S3, and why every game studio should own their git infra."
|
||||
tags: ["gitea", "lfs", "gamedev", "devops", "git", "go"]
|
||||
---
|
||||
|
||||
Our main game repo is **37 GB**. A single character model can be 500 MB. A Unity scene with all dependencies pushes past 2 GB. GitHub's LFS gives you 1 GB free and charges by the gig after that. For a three-person indie studio shipping a procedural colony sim, that math doesn't work.
|
||||
|
||||
So we forked Gitea. Here's why, how, and what we're building on top of it.
|
||||
|
||||
---
|
||||
|
||||
## The Problem: Game Assets Don't Fit in Normal Git
|
||||
|
||||
Web development repos are kilobytes of text. Game development repos are gigabytes of binary. A typical Ariki build includes:
|
||||
|
||||
| Asset Type | Typical Size |
|
||||
|-----------|-------------|
|
||||
| Character model (FBX + textures) | 200–500 MB |
|
||||
| Environment (terrain, foliage, materials) | 1–3 GB |
|
||||
| Audio (FMOD project, stems, ambience) | 500 MB–2 GB |
|
||||
| Unity/Godot scene with dependencies | 500 MB–2 GB |
|
||||
| Build artifacts (per platform) | 1–5 GB |
|
||||
|
||||
That's before you count iteration. We iterate fast — multiple builds per day, new art landing every few hours from the concept pipeline, audio revisions from the sound engineer. Git without LFS would balloon the `.git` folder to hundreds of gigabytes and make `git clone` a coffee-break-length operation.
|
||||
|
||||
**GitHub LFS pricing** at our scale would cost hundreds per month — and that's before you factor in the 1 GB free tier being exhausted by a single texture pack.
|
||||
|
||||
---
|
||||
|
||||
## The Solution: Self-Hosted Gitea with S3 LFS
|
||||
|
||||
We run [Gitea](https://about.gitea.com/) — the open-source, self-hosted Git service — on a $13/month AWS Lightsail instance (2 vCPU, 2 GB RAM, 60 GB SSD). LFS blobs go to S3 (`tinqs-gitea-lfs` bucket in eu-west-1), costing roughly $1.60/month for storage and transfer.
|
||||
|
||||
```
|
||||
Developer machine Git Studio (Lightsail)
|
||||
│ │
|
||||
│ git push (LFS tracked) │
|
||||
├──────────────────────────────────────►│
|
||||
│ ├── git objects → SQLite (local SSD)
|
||||
│ ├── LFS blobs → S3 (tinqs-gitea-lfs)
|
||||
│ │
|
||||
│ git clone / git pull │
|
||||
◄───────────────────────────────────────┤
|
||||
│ (LFS blobs streamed from S3) │
|
||||
```
|
||||
|
||||
Total cost: **~$14.60/month** for unlimited LFS storage. For comparison, GitHub Teams is $4/user/month plus LFS data charges — at our scale, that's roughly **10x more expensive**.
|
||||
|
||||
---
|
||||
|
||||
## Why We Forked It
|
||||
|
||||
Gitea is great out of the box, but game development has specific needs that the upstream project doesn't prioritize:
|
||||
|
||||
### 1. Asset preview in the browser
|
||||
|
||||
When a designer pushes a new `.fbx` or `.png`, the team should be able to see it in the browser — not download it, find the right viewer, and open it. We're building a web-based 3D asset viewer (Three.js + WebGL) that renders models, textures, and materials directly in the repo browser.
|
||||
|
||||
```
|
||||
Repo view → Click a .fbx file → Three.js renders it inline
|
||||
→ Rotate, zoom, inspect materials
|
||||
→ "Approved" button for art review
|
||||
```
|
||||
|
||||
### 2. LFS UX improvements
|
||||
|
||||
Upstream Gitea's LFS UI is functional but sparse. We're adding:
|
||||
- **Per-file LFS tracking status** — see at a glance which files are tracked, their sizes, and when they were last pushed
|
||||
- **LFS storage breakdown** — which asset types consume the most space
|
||||
- **Batch operations** — migrate multiple files into/out of LFS in one click
|
||||
|
||||
### 3. Team Tool integration
|
||||
|
||||
Our [Team Tool](https://bot.arikigame.com/guide/team-tool-ui) (the local agent bus we use for voice transcription, screenshots, and agent coordination) will integrate directly with the forked Gitea:
|
||||
|
||||
- Push notifications land as breadcrumbs in the live transcript session
|
||||
- Agent chat can query repo state and file contents
|
||||
- Build status appears in the shared timeline
|
||||
|
||||
### 4. Platform SSO
|
||||
|
||||
Long-term: OAuth2-based single sign-on across Git Studio, the Team Tool, admin panel, and the Ariki game itself. One account, everywhere.
|
||||
|
||||
---
|
||||
|
||||
## The Architecture
|
||||
|
||||
Our fork lives at `tinqs-ltd/tinqs-git` on Git Studio. It tracks upstream Gitea via periodic rebase:
|
||||
|
||||
```
|
||||
upstream (github.com/go-gitea/gitea) ──rebase──► main
|
||||
│
|
||||
├── tinqs/main (our customizations)
|
||||
├── tinqs/phase-1 (branding)
|
||||
├── tinqs/phase-2 (LFS UX)
|
||||
├── tinqs/phase-3 (platform integration)
|
||||
└── tinqs/phase-8 (platform auth)
|
||||
```
|
||||
|
||||
We modify:
|
||||
- **Frontend** (Vue + TypeScript templates) — asset viewer, LFS UI, branding
|
||||
- **Backend** (Go) — new API routes, LFS improvements, auth modules
|
||||
- **Database** (SQLite via CGo) — compiled into the binary, no external DB
|
||||
|
||||
We **don't touch**:
|
||||
- Core Git operations, webhook dispatch, user management, migration — those stay upstream-clean for easy rebasing.
|
||||
|
||||
---
|
||||
|
||||
## Why Every Game Studio Should Own Their Git
|
||||
|
||||
Game development has unique constraints that hosted platforms don't (and shouldn't) solve:
|
||||
|
||||
1. **Asset size is orders of magnitude larger** — a web app's entire repo fits in one game asset. LFS is non-negotiable, and cloud LFS pricing adds up fast.
|
||||
|
||||
2. **Binary diffs don't compress** — every new version of a `.fbx` or `.psd` is a whole new blob. You need LFS to keep the repo cloneable.
|
||||
|
||||
3. **Iteration speed demands local or near-local git** — waiting 30 seconds for `git push` kills flow. Self-hosted on the same continent means LAN-speed pushes.
|
||||
|
||||
4. **Custom workflows** — art review in-browser, CI that understands Unity/Godot builds, automated asset pipeline triggers on push. These are game-specific and don't belong in a general-purpose git host.
|
||||
|
||||
5. **Cost predictability** — $14.60/month, flat. No per-gigabyte surprises when the art team ships a big update.
|
||||
|
||||
---
|
||||
|
||||
## What's Next
|
||||
|
||||
We're actively developing `tinqs-git` (our Gitea fork) in the open. The repo is on [Git Studio](https://git.arikigame.com/tinqs-ltd/tinqs-git) — currently private during development, going public when the asset viewer ships. The fork stays AGPL (upstream Gitea's license), and we'll upstream improvements that make sense for the broader community.
|
||||
|
||||
If you're a game dev team hitting the limits of hosted Git, self-hosting Gitea with S3 LFS is a weekend project that pays for itself in the first month.
|
||||
|
||||
---
|
||||
|
||||
**Questions?** Find us at [arikigame.com](https://arikigame.com) or [tinqs.com/blog](https://tinqs.com/blog).
|
||||
|
||||
---
|
||||
|
||||
*Ozan Bozkurt is CTO at Tinqs, building Ariki — a Polynesian survival colony sim. He writes about game dev infrastructure, AI tooling, and shipping with small teams.*
|
||||
@@ -1,25 +0,0 @@
|
||||
import { defineConfig, globalIgnores } from "eslint/config";
|
||||
import nextVitals from "eslint-config-next/core-web-vitals";
|
||||
import nextTs from "eslint-config-next/typescript";
|
||||
|
||||
const eslintConfig = defineConfig([
|
||||
...nextVitals,
|
||||
...nextTs,
|
||||
// Static export — <a> and <img> are fine, Next.js Image/Link don't apply
|
||||
{
|
||||
rules: {
|
||||
"@next/next/no-html-link-for-pages": "off",
|
||||
"@next/next/no-img-element": "off",
|
||||
},
|
||||
},
|
||||
// Override default ignores of eslint-config-next.
|
||||
globalIgnores([
|
||||
// Default ignores of eslint-config-next:
|
||||
".next/**",
|
||||
"out/**",
|
||||
"build/**",
|
||||
"next-env.d.ts",
|
||||
]),
|
||||
]);
|
||||
|
||||
export default eslintConfig;
|
||||
@@ -1,5 +0,0 @@
|
||||
# tinqs.com
|
||||
|
||||
Tinqs company website — Next.js static export, S3 + CloudFront.
|
||||
|
||||
**Repo:** [git.arikigame.com/tinqs-ltd/website](https://git.arikigame.com/tinqs-ltd/website)
|
||||
@@ -1,8 +0,0 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: "export",
|
||||
trailingSlash: true,
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "website",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint"
|
||||
},
|
||||
"dependencies": {
|
||||
"gray-matter": "^4.0.3",
|
||||
"next": "^16.2.3",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"react-markdown": "^10.1.0",
|
||||
"remark-gfm": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "^16.2.3",
|
||||
"tailwindcss": "^4",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
const config = {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -1,4 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<rect width="32" height="32" rx="6" fill="#0a0a0f"/>
|
||||
<text x="16" y="23" font-family="Georgia, serif" font-size="18" font-weight="bold" fill="#c9935a" text-anchor="middle">T</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 253 B |
|
Before Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 165 KiB |
|
Before Width: | Height: | Size: 206 KiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 230 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 143 KiB |
|
Before Width: | Height: | Size: 658 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 2.9 MiB |
@@ -1,3 +0,0 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Sitemap: https://www.tinqs.com/sitemap.xml
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://www.tinqs.com</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
@@ -1,80 +0,0 @@
|
||||
import { getAllSlugs, getPostBySlug } from "@/lib/posts";
|
||||
import { notFound } from "next/navigation";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export function generateStaticParams() {
|
||||
return getAllSlugs().map((slug) => ({ slug }));
|
||||
}
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ slug: string }>;
|
||||
}): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const post = getPostBySlug(slug);
|
||||
if (!post) return { title: "Not Found" };
|
||||
|
||||
return {
|
||||
title: `${post.title} — Tinqs Blog`,
|
||||
description: post.excerpt,
|
||||
openGraph: {
|
||||
title: post.title,
|
||||
description: post.excerpt,
|
||||
type: "article",
|
||||
authors: [post.author],
|
||||
publishedTime: post.date,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default async function BlogPostPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ slug: string }>;
|
||||
}) {
|
||||
const { slug } = await params;
|
||||
const post = getPostBySlug(slug);
|
||||
|
||||
if (!post) notFound();
|
||||
|
||||
return (
|
||||
<main className="blog-post">
|
||||
<div className="blog-post__inner">
|
||||
<Link href="/blog" className="blog-post__back">
|
||||
← All posts
|
||||
</Link>
|
||||
|
||||
<header className="blog-post__header">
|
||||
<h1 className="blog-post__title">{post.title}</h1>
|
||||
<div className="blog-post__meta">
|
||||
<span className="blog-post__author">{post.author}</span>
|
||||
<time className="blog-post__date">
|
||||
{new Date(post.date).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</time>
|
||||
</div>
|
||||
<div className="blog-post__tags">
|
||||
{post.tags.map((tag) => (
|
||||
<span key={tag} className="blog-post__tag">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="blog-post__content prose">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||
{post.content}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import { getAllPosts } from "@/lib/posts";
|
||||
import Link from "next/link";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Blog — Tinqs",
|
||||
description:
|
||||
"Writing about AI tooling, game dev, and shipping fast with small teams.",
|
||||
};
|
||||
|
||||
export default function BlogPage() {
|
||||
const posts = getAllPosts();
|
||||
|
||||
return (
|
||||
<main className="blog-list">
|
||||
<div className="blog-list__inner">
|
||||
<h1 className="blog-list__title">Blog</h1>
|
||||
<p className="blog-list__subtitle">
|
||||
AI tooling, game dev, and shipping fast with small teams.
|
||||
</p>
|
||||
|
||||
<div className="blog-list__grid">
|
||||
{posts.map((post) => (
|
||||
<Link
|
||||
key={post.slug}
|
||||
href={`/blog/${post.slug}`}
|
||||
className="blog-card"
|
||||
>
|
||||
<article>
|
||||
<div className="blog-card__meta">
|
||||
{post.tags.map((tag) => (
|
||||
<span key={tag} className="blog-card__tag">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
<time className="blog-card__date">
|
||||
{new Date(post.date).toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</time>
|
||||
</div>
|
||||
<h2 className="blog-card__title">{post.title}</h2>
|
||||
<p className="blog-card__excerpt">{post.excerpt}</p>
|
||||
<span className="blog-card__author">{post.author}</span>
|
||||
</article>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 25 KiB |
@@ -1,124 +0,0 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Inter, Libre_Baskerville } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const inter = Inter({
|
||||
variable: "--f-body",
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
const libreBaskerville = Libre_Baskerville({
|
||||
variable: "--f-head",
|
||||
subsets: ["latin"],
|
||||
weight: ["400", "700"],
|
||||
style: ["normal", "italic"],
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Tinqs — Polynesian survival game studio | London",
|
||||
description:
|
||||
"London indie studio making Ariki — a Polynesian survival colony sim. Rise from castaway to chieftain across a Pacific archipelago. Co-op multiplayer, Early Access.",
|
||||
keywords: [
|
||||
"Tinqs",
|
||||
"indie game studio",
|
||||
"Ariki",
|
||||
"Polynesian survival game",
|
||||
"island colony builder",
|
||||
"co-op survival sim",
|
||||
"Pacific island exploration",
|
||||
"tribe management",
|
||||
"village builder",
|
||||
"Early Access",
|
||||
"Steam",
|
||||
],
|
||||
authors: [{ name: "Tinqs Limited" }],
|
||||
openGraph: {
|
||||
type: "website",
|
||||
url: "https://www.tinqs.com/",
|
||||
title: "Tinqs — Polynesian survival game studio",
|
||||
description:
|
||||
"The tribe is the game. Indie studio from London crafting Ariki.",
|
||||
siteName: "Tinqs",
|
||||
images: [
|
||||
{
|
||||
url: "https://www.tinqs.com/img/og-cover.jpg",
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: "Ariki — a Polynesian survival colony sim",
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: "Tinqs — Polynesian survival game studio",
|
||||
description:
|
||||
"The tribe is the game. Indie studio from London crafting Ariki.",
|
||||
images: ["https://www.tinqs.com/img/og-cover.jpg"],
|
||||
},
|
||||
metadataBase: new URL("https://www.tinqs.com"),
|
||||
alternates: {
|
||||
canonical: "/",
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" className={`${inter.variable} ${libreBaskerville.variable}`}>
|
||||
<head>
|
||||
<link rel="icon" type="image/svg+xml" href="/img/favicon.svg" />
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify({
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
name: "Tinqs Limited",
|
||||
url: "https://www.tinqs.com",
|
||||
logo: "https://www.tinqs.com/img/favicon.svg",
|
||||
foundingDate: "2020",
|
||||
description:
|
||||
"Indie game studio crafting Ariki — a Polynesian survival colony sim set in a Pacific archipelago.",
|
||||
email: "hello@tinqs.com",
|
||||
address: {
|
||||
"@type": "PostalAddress",
|
||||
addressLocality: "London",
|
||||
addressCountry: "UK",
|
||||
},
|
||||
sameAs: ["https://arikigame.com"],
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: JSON.stringify({
|
||||
"@context": "https://schema.org",
|
||||
"@type": "VideoGame",
|
||||
name: "Ariki",
|
||||
alternateName: "Ariki",
|
||||
url: "https://arikigame.com",
|
||||
description:
|
||||
"A Polynesian survival colony sim. Rise from castaway to chieftain across a Pacific archipelago.",
|
||||
genre: ["Survival", "Colony Sim", "Action RPG"],
|
||||
gamePlatform: ["PC", "PlayStation", "Xbox"],
|
||||
applicationCategory: "Game",
|
||||
operatingSystem: ["Windows", "macOS"],
|
||||
author: {
|
||||
"@type": "Organization",
|
||||
name: "Tinqs Limited",
|
||||
url: "https://www.tinqs.com",
|
||||
},
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -1,383 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
/* ── SVG Icon Components ── */
|
||||
|
||||
function SteamIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11.979 0C5.678 0 .511 4.86.022 11.037l6.432 2.658c.545-.371 1.203-.59 1.912-.59.063 0 .125.004.188.006l2.861-4.142V8.91c0-2.495 2.028-4.524 4.524-4.524 2.494 0 4.524 2.031 4.524 4.527s-2.03 4.525-4.524 4.525h-.105l-4.076 2.911c0 .052.004.105.004.159 0 1.875-1.515 3.396-3.39 3.396-1.635 0-3.016-1.173-3.331-2.727L.436 15.27C1.862 20.307 6.486 24 11.979 24c6.627 0 11.999-5.373 11.999-12S18.605 0 11.979 0zM7.54 18.21l-1.473-.61c.262.543.714.999 1.314 1.25 1.297.539 2.793-.076 3.332-1.375.263-.63.264-1.319.005-1.949s-.75-1.121-1.377-1.383c-.624-.26-1.29-.249-1.878-.03l1.523.63c.956.4 1.409 1.5 1.009 2.455-.397.957-1.497 1.41-2.454 1.012H7.54zm11.415-9.303c0-1.662-1.353-3.015-3.015-3.015-1.665 0-3.015 1.353-3.015 3.015 0 1.665 1.35 3.015 3.015 3.015 1.663 0 3.015-1.35 3.015-3.015zm-5.273-.005c0-1.252 1.013-2.266 2.265-2.266 1.249 0 2.266 1.014 2.266 2.266 0 1.251-1.017 2.265-2.266 2.265-1.253 0-2.265-1.014-2.265-2.265z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function DiscordIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20.32 4.37a19.8 19.8 0 0 0-4.88-1.52.07.07 0 0 0-.08.04c-.21.38-.45.87-.61 1.26a18.27 18.27 0 0 0-5.5 0 12.64 12.64 0 0 0-.63-1.26.08.08 0 0 0-.08-.04 19.74 19.74 0 0 0-4.88 1.52.07.07 0 0 0-.03.03C1.07 8.7.32 12.88.7 17.01a.08.08 0 0 0 .03.06 19.9 19.9 0 0 0 5.99 3.03.08.08 0 0 0 .08-.03c.46-.63.87-1.3 1.22-2a.08.08 0 0 0-.04-.11 13.1 13.1 0 0 1-1.87-.9.08.08 0 0 1 0-.13c.13-.09.25-.19.37-.29a.08.08 0 0 1 .08-.01c3.93 1.8 8.18 1.8 12.07 0a.08.08 0 0 1 .08 0c.12.1.25.2.37.3a.08.08 0 0 1 0 .12c-.6.35-1.22.65-1.88.9a.08.08 0 0 0-.04.11c.36.7.77 1.37 1.22 2a.08.08 0 0 0 .08.03 19.83 19.83 0 0 0 6-3.03.08.08 0 0 0 .04-.05c.45-4.69-.76-8.83-3.2-12.61a.06.06 0 0 0-.04-.03ZM8.02 14.33c-1.09 0-1.98-1-1.98-2.23s.87-2.23 1.98-2.23c1.12 0 2 1.01 1.98 2.23 0 1.23-.87 2.23-1.98 2.23Zm7.32 0c-1.09 0-1.98-1-1.98-2.23s.87-2.23 1.98-2.23c1.12 0 2 1.01 1.98 2.23 0 1.23-.87 2.23-1.98 2.23Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function XIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M18.24 2.25h3.31l-7.23 8.26 8.51 11.24h-6.66l-5.21-6.82-5.97 6.82H1.68l7.73-8.84L1.17 2.25h6.83l4.72 6.24 5.52-6.24Zm-1.16 17.52h1.83L7.08 4.13H5.11l11.97 15.64Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function YouTubeIcon() {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M23.5 6.19a3.02 3.02 0 0 0-2.12-2.14C19.5 3.5 12 3.5 12 3.5s-7.5 0-9.38.55A3.02 3.02 0 0 0 .5 6.19 31.56 31.56 0 0 0 0 12a31.56 31.56 0 0 0 .5 5.81 3.02 3.02 0 0 0 2.12 2.14c1.88.55 9.38.55 9.38.55s7.5 0 9.38-.55a3.02 3.02 0 0 0 2.12-2.14A31.56 31.56 0 0 0 24 12a31.56 31.56 0 0 0-.5-5.81ZM9.55 15.57V8.43L15.82 12l-6.27 3.57Z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
/* ── Page Component ── */
|
||||
|
||||
export default function Home() {
|
||||
useEffect(() => {
|
||||
// Nav scroll effect
|
||||
const nav = document.getElementById("nav");
|
||||
const handleScroll = () => {
|
||||
nav?.classList.toggle("nav--scrolled", window.scrollY > 60);
|
||||
};
|
||||
window.addEventListener("scroll", handleScroll, { passive: true });
|
||||
|
||||
// Mobile menu toggle
|
||||
const burger = document.getElementById("navBurger");
|
||||
const mobileMenu = document.getElementById("mobileMenu");
|
||||
const toggleMenu = () => {
|
||||
const open = mobileMenu?.classList.toggle("mobile-menu--open");
|
||||
burger?.classList.toggle("nav__burger--open", open ?? false);
|
||||
document.body.style.overflow = open ? "hidden" : "";
|
||||
};
|
||||
burger?.addEventListener("click", toggleMenu);
|
||||
mobileMenu?.querySelectorAll("a").forEach((link) => {
|
||||
link.addEventListener("click", () => {
|
||||
mobileMenu?.classList.remove("mobile-menu--open");
|
||||
burger?.classList.remove("nav__burger--open");
|
||||
document.body.style.overflow = "";
|
||||
});
|
||||
});
|
||||
|
||||
// Fade-in on scroll
|
||||
const faders = document.querySelectorAll(
|
||||
".about, .pillars, .gallery__row, .world, .signup"
|
||||
);
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((e) => {
|
||||
if (e.isIntersecting) e.target.classList.add("visible");
|
||||
});
|
||||
},
|
||||
{ threshold: 0.1 }
|
||||
);
|
||||
faders.forEach((el) => observer.observe(el));
|
||||
|
||||
// Parallax on gallery images
|
||||
const parallaxEls = document.querySelectorAll(
|
||||
".gallery__img-wrap--parallax img"
|
||||
);
|
||||
const handleParallax = () => {
|
||||
parallaxEls.forEach((img) => {
|
||||
const rect = (img as HTMLElement).parentElement!.getBoundingClientRect();
|
||||
const speed = 0.08;
|
||||
const yOffset = (rect.top - window.innerHeight / 2) * speed;
|
||||
(img as HTMLElement).style.transform = `translateY(${yOffset}px) scale(1.08)`;
|
||||
});
|
||||
};
|
||||
window.addEventListener("scroll", handleParallax, { passive: true });
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
window.removeEventListener("scroll", handleParallax);
|
||||
burger?.removeEventListener("click", toggleMenu);
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* ── NAV ── */}
|
||||
<nav className="nav" id="nav">
|
||||
<a href="/" className="nav__logo" aria-label="Tinqs home">
|
||||
<span className="nav__wordmark">TINQS</span>
|
||||
</a>
|
||||
<div className="nav__center">
|
||||
<a href="#game" className="nav__link">Games</a>
|
||||
<a href="#about" className="nav__link">About</a>
|
||||
<a href="/blog" className="nav__link">Blog</a>
|
||||
<a href="#careers" className="nav__link">Careers</a>
|
||||
<a href="#signup" className="nav__link">Contact</a>
|
||||
</div>
|
||||
<div className="nav__socials">
|
||||
<a href="https://store.steampowered.com/developer/tinqsstudio" target="_blank" rel="noopener noreferrer" className="nav__social-icon" aria-label="Steam" title="Steam"><SteamIcon /></a>
|
||||
<a href="#" className="nav__social-icon" aria-label="Discord" title="Discord"><DiscordIcon /></a>
|
||||
<a href="#" className="nav__social-icon" aria-label="X" title="X"><XIcon /></a>
|
||||
<a href="#" className="nav__social-icon" aria-label="YouTube" title="YouTube"><YouTubeIcon /></a>
|
||||
</div>
|
||||
<button className="nav__burger" aria-label="Open menu" id="navBurger">
|
||||
<span></span><span></span><span></span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
{/* ── MOBILE MENU ── */}
|
||||
<div className="mobile-menu" id="mobileMenu">
|
||||
<a href="#game" className="mobile-menu__link">Games</a>
|
||||
<a href="#about" className="mobile-menu__link">About</a>
|
||||
<a href="/blog" className="mobile-menu__link">Blog</a>
|
||||
<a href="#careers" className="mobile-menu__link">Careers</a>
|
||||
<a href="#signup" className="mobile-menu__link">Contact</a>
|
||||
</div>
|
||||
|
||||
{/* ── HERO ── */}
|
||||
<header className="hero" id="hero">
|
||||
<div className="hero__bg" aria-hidden="true" />
|
||||
<div className="hero__vignette" aria-hidden="true" />
|
||||
<div className="hero__content">
|
||||
<h1 className="hero__title">The tribe is the game.</h1>
|
||||
<p className="hero__subtitle">Rise from castaway to chieftain</p>
|
||||
<span className="hero__badge">Early Access · Coming Soon</span>
|
||||
<br />
|
||||
<a href="#game" className="hero__cta">Explore</a>
|
||||
</div>
|
||||
<div className="hero__scroll-hint" aria-hidden="true">
|
||||
<span></span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* ── ABOUT ── */}
|
||||
<section className="about" id="about">
|
||||
<div className="about__inner">
|
||||
<span className="section-label">The Studio</span>
|
||||
<h2 className="about__title">
|
||||
Indie game-makers from London.
|
||||
<br />
|
||||
Three people, one world, one voyage.
|
||||
</h2>
|
||||
<p className="about__text">
|
||||
Tinqs is a three-person indie team crafting Ariki — a survival
|
||||
colony sim set in a Polynesian-inspired archipelago, where every island
|
||||
hides spirits, resources, and stories waiting to be uncovered.
|
||||
</p>
|
||||
<a href="#careers" className="about__hiring">We’re Hiring →</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── CAREERS ── */}
|
||||
<section className="careers" id="careers">
|
||||
<div className="careers__inner">
|
||||
<h2 className="careers__title">Join the crew</h2>
|
||||
<p className="careers__text">
|
||||
We’re looking for people who care about craft. If you want to help
|
||||
build a world worth exploring, get in touch.
|
||||
</p>
|
||||
<a href="mailto:hello@tinqs.com" className="careers__cta">Apply Now</a>
|
||||
<span className="careers__email">or email hello@tinqs.com</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── THREE PILLARS ── */}
|
||||
<section className="pillars" id="game">
|
||||
<span className="section-label">Ariki</span>
|
||||
<h2 className="pillars__title">Survive. Build. Explore.</h2>
|
||||
<div className="pillars__grid">
|
||||
<article className="pillar">
|
||||
<div className="pillar__img-wrap">
|
||||
<img
|
||||
src="/img/scene-survive-cooking.png"
|
||||
alt="Tribal character cooking fish under a thatched shelter at golden hour"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<h3 className="pillar__name">Survive</h3>
|
||||
<p className="pillar__desc">
|
||||
Forage, hunt, and cook to keep your tribe alive. Every resource
|
||||
matters on an uncharted island.
|
||||
</p>
|
||||
</article>
|
||||
<article className="pillar">
|
||||
<div className="pillar__img-wrap">
|
||||
<img
|
||||
src="/img/scene-build-settlement.png"
|
||||
alt="Aerial view of a thriving island settlement with huts, farms, and palm trees"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<h3 className="pillar__name">Build</h3>
|
||||
<p className="pillar__desc">
|
||||
Grow from a makeshift camp to a thriving colony. Hundreds of
|
||||
villagers, working as one.
|
||||
</p>
|
||||
</article>
|
||||
<article className="pillar">
|
||||
<div className="pillar__img-wrap">
|
||||
<img
|
||||
src="/img/scene-explore-canoe.png"
|
||||
alt="Character standing beside an outrigger canoe on a tropical beach"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<h3 className="pillar__name">Explore</h3>
|
||||
<p className="pillar__desc">
|
||||
Craft a canoe and voyage to new islands. Each one holds unique
|
||||
spirits, dangers, and rewards.
|
||||
</p>
|
||||
</article>
|
||||
</div>
|
||||
<span className="pillars__meta">
|
||||
PC · Console · Co-op Multiplayer · Early Access
|
||||
</span>
|
||||
<br />
|
||||
<a href="https://arikigame.com" className="pillars__game-link">
|
||||
Explore Ariki →
|
||||
</a>
|
||||
</section>
|
||||
|
||||
{/* ── CINEMATIC GALLERY ── */}
|
||||
<section className="gallery" id="gallery">
|
||||
<div className="gallery__row">
|
||||
<div className="gallery__img-wrap gallery__img-wrap--parallax">
|
||||
<img
|
||||
src="/img/gallery/gallery-rice-paddy.png"
|
||||
alt="Character walking through terraced rice fields with lotus ponds and bamboo"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div className="gallery__text">
|
||||
<span className="section-label">Shape the Land</span>
|
||||
<p className="gallery__caption">
|
||||
Terrace the hillsides. Cultivate rice paddies. Transform wild
|
||||
jungle into a flourishing homeland — from Micronesian huts to
|
||||
Balinese stone temples.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gallery__row gallery__row--reverse">
|
||||
<div className="gallery__img-wrap gallery__img-wrap--parallax">
|
||||
<img
|
||||
src="/img/gallery/gallery-hillside-path.png"
|
||||
alt="Character on a dirt trail with palm trees and terraced fields at golden hour"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div className="gallery__text">
|
||||
<span className="section-label">Chart the Unknown</span>
|
||||
<p className="gallery__caption">
|
||||
Every island is a new frontier. Trek through palm forests, volcanic
|
||||
ridges, and hidden valleys to discover resources and ancient shrines.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gallery__row">
|
||||
<div className="gallery__img-wrap gallery__img-wrap--parallax">
|
||||
<img
|
||||
src="/img/gallery/gallery-forest-overlook.png"
|
||||
alt="Character with a staff looking over a forested village from a rocky overlook"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div className="gallery__text">
|
||||
<span className="section-label">Lead Your People</span>
|
||||
<p className="gallery__caption">
|
||||
You are the chief. Guide your tribe through storms, spirit
|
||||
encounters, and the long voyage toward a place to call home.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── THE WORLD ── */}
|
||||
<section className="world" id="world">
|
||||
<div className="world__bg" aria-hidden="true" />
|
||||
<div className="world__inner">
|
||||
<span className="section-label">The World</span>
|
||||
<h2 className="world__title">
|
||||
Inspired by the islands of Southeast Asia and the Pacific
|
||||
</h2>
|
||||
<p className="world__text">
|
||||
Voyaging cultures, living spirits, and an archipelago procedurally
|
||||
different every time you land. Where the Pacific meets something
|
||||
ancient.
|
||||
</p>
|
||||
<div className="world__map">
|
||||
<img
|
||||
src="/img/world-map-gold.jpg"
|
||||
alt="Isleborn volcanic island with village, waterfalls, and turquoise ocean"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── CONNECT ── */}
|
||||
<section className="connect" id="connect">
|
||||
<span className="section-label">Community</span>
|
||||
<h2 className="connect__title">Follow the voyage</h2>
|
||||
<div className="connect__links">
|
||||
<a href="https://store.steampowered.com/developer/tinqsstudio" target="_blank" rel="noopener noreferrer" className="connect__link">
|
||||
<SteamIcon />
|
||||
<span className="connect__link-label">Steam</span>
|
||||
</a>
|
||||
<a href="#" className="connect__link">
|
||||
<DiscordIcon />
|
||||
<span className="connect__link-label">Discord</span>
|
||||
</a>
|
||||
<a href="#" className="connect__link">
|
||||
<XIcon />
|
||||
<span className="connect__link-label">X</span>
|
||||
</a>
|
||||
<a href="#" className="connect__link">
|
||||
<YouTubeIcon />
|
||||
<span className="connect__link-label">YouTube</span>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── SIGNUP ── */}
|
||||
<section className="signup" id="signup">
|
||||
<div className="signup__bg" aria-hidden="true" />
|
||||
<div className="signup__overlay" aria-hidden="true" />
|
||||
<div className="signup__inner">
|
||||
<span className="section-label">Stay in the Loop</span>
|
||||
<h2 className="signup__title">Early Access is coming.</h2>
|
||||
<p className="signup__subtitle">Be first to know.</p>
|
||||
<form className="signup__form" action="#" method="POST">
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
placeholder="your@email.com"
|
||||
required
|
||||
aria-label="Email address"
|
||||
/>
|
||||
<button type="submit" className="btn">Stay Close</button>
|
||||
</form>
|
||||
<p className="signup__note">No spam. Only milestones.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* ── FOOTER ── */}
|
||||
<footer className="footer">
|
||||
<div className="footer__inner">
|
||||
<span className="footer__wordmark">TINQS</span>
|
||||
<div className="footer__links">
|
||||
<a href="#game">Games</a>
|
||||
<a href="#about">About</a>
|
||||
<a href="/blog">Blog</a>
|
||||
<a href="#careers">Careers</a>
|
||||
<a href="mailto:hello@tinqs.com">hello@tinqs.com</a>
|
||||
<a href="/press">Press Kit</a>
|
||||
</div>
|
||||
<p className="footer__copy">
|
||||
Tinqs Limited — London, est. 2020
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Tinqs Team Tool — Setup",
|
||||
description: "Install the Tinqs Team Tool for live transcription and team collaboration.",
|
||||
};
|
||||
|
||||
export default function TeamToolPage() {
|
||||
return (
|
||||
<div style={{ background: "#0a0a0a", minHeight: "100vh", color: "#e0e0e0", fontFamily: "system-ui, -apple-system, sans-serif" }}>
|
||||
<main style={{ maxWidth: 720, margin: "0 auto", padding: "40px 24px", lineHeight: 1.6 }}>
|
||||
|
||||
<div style={{ marginBottom: 32 }}>
|
||||
<h1 style={{ fontSize: 28, fontWeight: 700, marginBottom: 4, color: "#fff" }}>
|
||||
Tinqs Team Tool
|
||||
</h1>
|
||||
<p style={{ color: "#888", fontSize: 14, margin: 0 }}>
|
||||
Live transcription, dual-engine (Edge + ElevenLabs), speaker diarization, screenshots, team chat.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* ── Prerequisites ── */}
|
||||
<Section title="Before you start" color="#ef4444">
|
||||
<ol style={ol}>
|
||||
<li>
|
||||
<strong style={{ color: "#fca5a5" }}>Uninstall Tailscale</strong> — we no longer use it.
|
||||
<br />
|
||||
<span style={{ color: "#6b7280", fontSize: 12 }}>
|
||||
Windows: Settings → Apps → Tailscale → Uninstall.
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Clear old bash/shell records</strong> — remove any Tailscale references from your shell config:
|
||||
<Code>{`# PowerShell: remove Tailscale from PATH if added manually
|
||||
# Check your $PROFILE for any Tailscale lines:
|
||||
notepad $PROFILE`}</Code>
|
||||
</li>
|
||||
<li>
|
||||
<strong>Restart your machine</strong> — clean slate before installing.
|
||||
</li>
|
||||
</ol>
|
||||
</Section>
|
||||
|
||||
{/* ── Install ── */}
|
||||
<Section title="Install the Team Tool" color="#fbbf24">
|
||||
<h3 style={h3}>Option A — One-line install (recommended)</h3>
|
||||
<p style={{ color: "#9ca3af", fontSize: 13, marginBottom: 8 }}>
|
||||
Open <strong>PowerShell</strong> and paste:
|
||||
</p>
|
||||
<Code>{`irm https://bot.arikigame.com/setup-windows.ps1 | iex`}</Code>
|
||||
|
||||
<h3 style={h3}>Option B — Manual download</h3>
|
||||
<ol style={ol}>
|
||||
<li>
|
||||
Download{" "}
|
||||
<A href="https://tinqs-cli-releases.s3.eu-west-1.amazonaws.com/team-tool/latest/TinqsTeamTool.exe">
|
||||
TinqsTeamTool.exe
|
||||
</A>
|
||||
</li>
|
||||
<li>Install ffmpeg: <Code inline>{`winget install ffmpeg`}</Code></li>
|
||||
<li>Run <code style={code}>TinqsTeamTool.exe</code></li>
|
||||
</ol>
|
||||
</Section>
|
||||
|
||||
{/* ── Setup Token ── */}
|
||||
<Section title="Get your token" color="#60a5fa">
|
||||
<ol style={ol}>
|
||||
<li>
|
||||
Go to{" "}
|
||||
<A href="https://git.arikigame.com/user/settings/applications">
|
||||
Git Studio → Settings → Applications
|
||||
</A>
|
||||
</li>
|
||||
<li>Token Name: <code style={code}>tinqs-team-tool-yourname</code></li>
|
||||
<li>Permissions: select all → Generate Token</li>
|
||||
<li>Copy the token — you{"'"}ll need it on first launch</li>
|
||||
</ol>
|
||||
<p style={{ color: "#6b7280", fontSize: 12, marginTop: 8 }}>
|
||||
Can{"'"}t access Git Studio? Ask Ozan or Jeremy for an invite.
|
||||
</p>
|
||||
</Section>
|
||||
|
||||
{/* ── What you get ── */}
|
||||
<Section title="What the tool does" color="#10b981">
|
||||
<ul style={{ ...ol, listStyleType: "none", paddingLeft: 0 }}>
|
||||
<li style={{ marginBottom: 6 }}><span style={{ color: "#fbbf24" }}>Left column</span> — Edge Web Speech (real-time, free, instant)</li>
|
||||
<li style={{ marginBottom: 6 }}><span style={{ color: "#60a5fa" }}>Right column</span> — ElevenLabs Scribe V2 (5s chunks, speaker IDs, higher accuracy)</li>
|
||||
<li style={{ marginBottom: 6 }}>Screenshot bar — capture Unity, Chrome, Full Screen, or All Windows</li>
|
||||
<li style={{ marginBottom: 6 }}>Language switch — English / Turkish</li>
|
||||
<li style={{ marginBottom: 6 }}>Auto-updates — checks every 5 min, one-click apply</li>
|
||||
</ul>
|
||||
</Section>
|
||||
|
||||
{/* ── Troubleshooting ── */}
|
||||
<Section title="Troubleshooting" color="#f97316">
|
||||
<table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
|
||||
<tbody>
|
||||
<Tr label="No mic">Windows Settings → Privacy → Microphone → Allow</Tr>
|
||||
<Tr label="No blue column">ELEVENLABS_API_KEY not set — ask Ozan or Jeremy</Tr>
|
||||
<Tr label="ffmpeg error"><code style={code}>winget install ffmpeg</code> then restart</Tr>
|
||||
<Tr label="Won't start">Kill old instance: <code style={code}>taskkill /f /im TinqsTeamTool.exe</code></Tr>
|
||||
<Tr label="Can't reach Git Studio">Make sure you can open <A href="https://git.arikigame.com">git.arikigame.com</A> in your browser</Tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</Section>
|
||||
|
||||
<div style={{ borderTop: "1px solid #222", marginTop: 40, paddingTop: 16, color: "#444", fontSize: 12 }}>
|
||||
Tinqs Team Tool ·{" "}
|
||||
<A href="https://git.arikigame.com/tinqs-ltd/bot">source</A> ·{" "}
|
||||
<A href="https://git.arikigame.com/tinqs-ltd/bot/issues">issues</A> ·{" "}
|
||||
<A href="https://www.tinqs.com">tinqs.com</A>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Section({ title, color, children }: { title: string; color: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<section style={{ marginBottom: 20, padding: "14px 18px", background: "#111", borderRadius: 8, border: "1px solid #1a1a1a" }}>
|
||||
<h2 style={{ fontSize: 15, fontWeight: 600, color, margin: "0 0 10px" }}>{title}</h2>
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
function Code({ children, inline }: { children: string; inline?: boolean }) {
|
||||
if (inline) return <code style={{ background: "#1a1a22", padding: "1px 6px", borderRadius: 4, fontSize: 12, color: "#c084fc" }}>{children}</code>;
|
||||
return (
|
||||
<pre style={{ background: "#0d0d0d", border: "1px solid #222", borderRadius: 6, padding: "10px 14px", fontSize: 13, overflowX: "auto", fontFamily: "ui-monospace, Consolas, monospace", color: "#c084fc" }}>
|
||||
{children}
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
function A({ href, children }: { href: string; children: React.ReactNode }) {
|
||||
return <a href={href} target="_blank" rel="noopener noreferrer" style={{ color: "#60a5fa", textDecoration: "none" }}>{children}</a>;
|
||||
}
|
||||
function Tr({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<tr style={{ borderBottom: "1px solid #1a1a1a" }}>
|
||||
<td style={{ padding: "6px 10px 6px 0", color: "#fbbf24", fontWeight: 600, whiteSpace: "nowrap", verticalAlign: "top" }}>{label}</td>
|
||||
<td style={{ padding: "6px 0", color: "#9ca3af" }}>{children}</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
const ol: React.CSSProperties = { paddingLeft: 20, color: "#9ca3af", fontSize: 13 };
|
||||
const h3: React.CSSProperties = { fontSize: 14, fontWeight: 600, color: "#d1d5db", margin: "14px 0 6px" };
|
||||
const code: React.CSSProperties = { background: "#1a1a22", padding: "1px 6px", borderRadius: 4, fontSize: 12, color: "#c084fc" };
|
||||
@@ -1,67 +0,0 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import matter from "gray-matter";
|
||||
|
||||
const postsDirectory = path.join(process.cwd(), "content", "blog");
|
||||
|
||||
export interface BlogPost {
|
||||
slug: string;
|
||||
title: string;
|
||||
date: string;
|
||||
author: string;
|
||||
excerpt: string;
|
||||
tags: string[];
|
||||
content: string;
|
||||
}
|
||||
|
||||
export function getAllPosts(): BlogPost[] {
|
||||
const filenames = fs.readdirSync(postsDirectory);
|
||||
const posts = filenames
|
||||
.filter((f) => f.endsWith(".md"))
|
||||
.map((filename) => {
|
||||
const slug = filename.replace(/\.md$/, "");
|
||||
const fullPath = path.join(postsDirectory, filename);
|
||||
const fileContents = fs.readFileSync(fullPath, "utf8");
|
||||
const { data, content } = matter(fileContents);
|
||||
|
||||
return {
|
||||
slug,
|
||||
title: data.title ?? slug,
|
||||
date: data.date ?? "",
|
||||
author: data.author ?? "Tinqs",
|
||||
excerpt: data.excerpt ?? "",
|
||||
tags: data.tags ?? [],
|
||||
content,
|
||||
} as BlogPost;
|
||||
})
|
||||
.sort((a, b) => (a.date < b.date ? 1 : -1));
|
||||
|
||||
return posts;
|
||||
}
|
||||
|
||||
export function getPostBySlug(slug: string): BlogPost | null {
|
||||
try {
|
||||
const fullPath = path.join(postsDirectory, `${slug}.md`);
|
||||
const fileContents = fs.readFileSync(fullPath, "utf8");
|
||||
const { data, content } = matter(fileContents);
|
||||
|
||||
return {
|
||||
slug,
|
||||
title: data.title ?? slug,
|
||||
date: data.date ?? "",
|
||||
author: data.author ?? "Tinqs",
|
||||
excerpt: data.excerpt ?? "",
|
||||
tags: data.tags ?? [],
|
||||
content,
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function getAllSlugs(): string[] {
|
||||
const filenames = fs.readdirSync(postsDirectory);
|
||||
return filenames
|
||||
.filter((f) => f.endsWith(".md"))
|
||||
.map((f) => f.replace(/\.md$/, ""));
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts",
|
||||
"**/*.mts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||