308 lines
18 KiB
HTML
308 lines
18 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: blob: https://venice.ai https://outerface.venice.ai; font-src 'none'; connect-src https://api.venice.ai; media-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'">
|
|
<meta name="referrer" content="strict-origin">
|
|
<title>Havre</title>
|
|
<meta name="description" content="A harbor for characters. Talk to Venice's characters on your own key, keep characters in a file you own, and remix the ones you love into someone new, with credit.">
|
|
<link rel="canonical" href="https://havre.yolopreneur.lol/">
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:title" content="Havre: a harbor for characters">
|
|
<meta property="og:description" content="Talk to Venice's characters on your own key. No account, no server: nothing is kept but what you keep.">
|
|
<meta property="og:url" content="https://havre.yolopreneur.lol/">
|
|
<meta property="og:image" content="https://havre.yolopreneur.lol/shots/og.png">
|
|
<meta property="og:image:width" content="1200">
|
|
<meta property="og:image:height" content="630">
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="theme-color" content="#ffffff">
|
|
<link rel="icon" href="favicon.svg" type="image/svg+xml">
|
|
<link rel="alternate" type="text/plain" href="llms.txt" title="For agents">
|
|
<link rel="stylesheet" href="style.css">
|
|
<script type="module" src="app.js"></script>
|
|
</head>
|
|
<body>
|
|
<a class="skip" href="#chat">skip to the chat</a>
|
|
|
|
<header class="mast" id="top">
|
|
<h1>HAVRE</h1>
|
|
<p class="motto">a harbor for characters.<br>talk to venice characters on your own key.<br>no account. no server. nothing kept but what you keep.</p>
|
|
<nav class="nav" aria-label="On this page"><a href="#how">how it works</a> / <a href="#agents">for agents</a> / <a href="#costs">costs</a> / <a href="#plans">early access</a></nav>
|
|
</header>
|
|
|
|
<form class="keybar" id="keyform" autocomplete="off" aria-label="Your Venice API key">
|
|
<label for="keyin">VENICE API KEY</label>
|
|
<input id="keyin" type="password" autocomplete="off" spellcheck="false" placeholder="paste it here">
|
|
<button type="submit" class="btn inv" id="keysave">SAVE</button>
|
|
<label class="check"><input type="checkbox" id="keyremember"> remember on this device</label>
|
|
<button type="button" class="btn ghost-inv" id="keyforget" hidden>FORGET</button>
|
|
<span id="balance" class="balance" hidden></span>
|
|
<span id="keystatus" class="keystatus" role="status" aria-live="polite"></span>
|
|
<span class="keyfine">sent to api.venice.ai and nowhere else. <a href="https://venice.ai/settings/api" target="_blank" rel="noopener">get a key</a></span>
|
|
</form>
|
|
|
|
<main class="studio" id="studio">
|
|
<section class="chars" id="chars" aria-labelledby="chars-h">
|
|
<div class="sechead">
|
|
<h2 id="chars-h">CHARACTERS</h2>
|
|
<span class="meta">venice's public characters.</span>
|
|
</div>
|
|
<form class="filters" id="venform" role="search" aria-label="Venice characters">
|
|
<label class="sr" for="vsearch">Search Venice characters</label>
|
|
<input id="vsearch" type="search" placeholder="search by name, or #tag" autocomplete="off" maxlength="200">
|
|
<label class="pick">sort <select id="vsort">
|
|
<option value="featured">featured</option>
|
|
<option value="imports">most imported</option>
|
|
<option value="highestRating">top rated</option>
|
|
<option value="mostRecent">newest</option>
|
|
</select></label>
|
|
<label class="check"><input type="checkbox" id="vadult"> adult (18+)</label>
|
|
<button type="submit" class="btn" id="vgo">SEARCH</button>
|
|
</form>
|
|
<p id="vtags" class="tags" hidden></p>
|
|
<p id="venstatus" class="status" role="status" aria-live="polite"></p>
|
|
<ul id="vgrid" class="grid" aria-label="Venice characters"></ul>
|
|
<p class="more"><span id="vpager" hidden><button type="button" class="linkish" id="vprev">previous</button> <span id="vrange"></span> <button type="button" class="linkish" id="vnext">next</button> / </span><button type="button" class="linkish" id="plain">chat without a character</button> / <button type="button" class="linkish" id="import">import a card</button> (v1, v2, v3, json or png) / <button type="button" class="linkish" id="new">write one</button></p>
|
|
</section>
|
|
|
|
<section class="chat" id="chat" aria-labelledby="who-name">
|
|
<div class="sechead">
|
|
<h2 id="who-name">CHAT</h2>
|
|
<span class="meta" id="who-meta"></span>
|
|
</div>
|
|
<ol id="log" class="log" role="log" aria-live="polite" aria-label="Conversation"></ol>
|
|
<form id="composer" class="composer">
|
|
<div class="sayrow">
|
|
<label for="say" class="sr">Message</label>
|
|
<textarea id="say" rows="1" placeholder="say something" autocomplete="off" enterkeyhint="send"></textarea>
|
|
<button type="submit" class="btn solid" id="sendbtn">SEND</button>
|
|
</div>
|
|
<p id="keynote" class="keynote" hidden></p>
|
|
</form>
|
|
</section>
|
|
</main>
|
|
|
|
<section class="block harbor" id="harbor" aria-labelledby="harbor-h">
|
|
<div class="sechead">
|
|
<h2 id="harbor-h">YOUR HARBOR</h2>
|
|
<span class="meta">kept in this browser. export a file to keep a character anywhere else.</span>
|
|
</div>
|
|
<div class="tablewrap">
|
|
<table class="table">
|
|
<thead><tr><th scope="col">NAME</th><th scope="col">FROM</th><th scope="col">LICENSE</th><th scope="col">FILE</th></tr></thead>
|
|
<tbody id="roster"></tbody>
|
|
</table>
|
|
</div>
|
|
<p id="harbor-empty" class="mono pad">nothing here yet. pick one of venice's characters, import a card, or write one.</p>
|
|
</section>
|
|
|
|
<section class="block card" id="card" aria-labelledby="card-h">
|
|
<div class="sechead">
|
|
<h2 id="card-h">CARD</h2>
|
|
<span class="meta">the character as a file: edit it, remix it, export it.</span>
|
|
</div>
|
|
<p id="card-empty" class="mono pad">no character yet. the card shows up here once someone is in your harbor.</p>
|
|
<div id="card-body">
|
|
<div class="tabs" role="tablist" aria-label="Card">
|
|
<button type="button" role="tab" id="t-edit" aria-controls="p-edit" aria-selected="true">SHEET</button>
|
|
<button type="button" role="tab" id="t-remix" aria-controls="p-remix" aria-selected="false" tabindex="-1">REMIX</button>
|
|
<button type="button" role="tab" id="t-export" aria-controls="p-export" aria-selected="false" tabindex="-1">EXPORT</button>
|
|
</div>
|
|
|
|
<div class="panel" id="p-edit" role="tabpanel" aria-labelledby="t-edit">
|
|
<form id="sheet" class="sheet" autocomplete="off">
|
|
<div class="col">
|
|
<div class="portrait">
|
|
<span class="face big" id="sheet-face" aria-hidden="true"></span>
|
|
<p class="mono"><button type="button" class="linkish" id="portrait-pick">change portrait</button> / <button type="button" class="linkish" id="portrait-clear">remove</button></p>
|
|
<input type="file" id="portrait-file" accept="image/png,image/jpeg,image/webp,image/svg+xml" hidden>
|
|
</div>
|
|
<label>NAME <input name="name" maxlength="120" required></label>
|
|
<label>TAGLINE <input name="tagline" maxlength="200" placeholder="one line under the name"></label>
|
|
<p id="linked-note" class="note" hidden></p>
|
|
<label>GREETING <textarea name="greeting" rows="3" maxlength="4000" placeholder="the first thing they say. a blank line starts a new text."></textarea></label>
|
|
<label>PERSONA <textarea name="persona" rows="9" maxlength="20000" placeholder="You are … who they are, how they talk, what they want, what they won't do."></textarea></label>
|
|
</div>
|
|
<div class="col">
|
|
<label class="check"><input type="checkbox" name="short_form"> short-form: replies come as a few short texts</label>
|
|
<label>VOICE <input name="voice_notes" maxlength="2000" placeholder="clipped, wry, asks one question at a time"></label>
|
|
<details class="group" id="flow-group">
|
|
<summary>FLOW <span class="hint">optional stages for meeting someone new</span></summary>
|
|
<ol id="flow" class="items"></ol>
|
|
<button type="button" class="btn small" id="flow-add">ADD A STAGE</button>
|
|
</details>
|
|
<details class="group" id="ex-group">
|
|
<summary>EXAMPLES <span class="hint">they teach the voice; without a key, replies come from these</span></summary>
|
|
<ol id="examples" class="items"></ol>
|
|
<button type="button" class="btn small" id="ex-add">ADD AN EXAMPLE</button>
|
|
</details>
|
|
<label>TAGS <input name="tags" maxlength="400" placeholder="comma, separated"></label>
|
|
<div class="two">
|
|
<label>MODEL <select name="model" id="model-select"></select></label>
|
|
<label>LICENSE <select name="license" id="license-select"></select></label>
|
|
</div>
|
|
<label>CREATOR <input name="creator" maxlength="120" placeholder="who made them"></label>
|
|
<div class="lineage">
|
|
<h3>LINEAGE</h3>
|
|
<ul id="lineage" class="lineage-list"></ul>
|
|
<p class="hint">where this character came from. entries are only ever added.</p>
|
|
</div>
|
|
<button type="button" class="btn danger small" id="delete">REMOVE FROM HARBOR</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="panel" id="p-remix" role="tabpanel" aria-labelledby="t-remix" hidden>
|
|
<div class="remix">
|
|
<p class="lede">Write someone new from the idea of <b id="rx-from">this character</b>, and keep the credit.</p>
|
|
<ol class="steps">
|
|
<li><b>Distill.</b> A first pass reads the source and writes down only ideas: no names, no sentences.</li>
|
|
<li><b>Write.</b> A second pass never sees the source. It writes someone new from the ideas and your twist.</li>
|
|
<li><b>Check.</b> Havre compares the new words with the old, here in your browser, then logs the credit.</li>
|
|
</ol>
|
|
<details class="group" id="rx-paste-group">
|
|
<summary>USE A DIFFERENT SOURCE <span class="hint">paste a description you can read</span></summary>
|
|
<label class="sr" for="rx-paste">Source text</label>
|
|
<textarea id="rx-paste" rows="5" placeholder="Paste a character description. Name it on the first line."></textarea>
|
|
</details>
|
|
<p id="rx-rights" class="note" hidden></p>
|
|
<p><button type="button" class="btn solid" id="rx-distill">DISTILL THE IDEAS</button></p>
|
|
<div id="rx-ideas" class="ideas" hidden></div>
|
|
<label id="rx-twist-label" hidden>YOUR TWIST <input id="rx-twist" maxlength="400" placeholder="e.g. make them a ferry pilot"></label>
|
|
<p><button type="button" class="btn solid" id="rx-write" hidden>WRITE SOMEONE NEW</button></p>
|
|
<div id="rx-draft" class="draft" hidden></div>
|
|
<p id="rx-status" class="status" role="status" aria-live="polite"></p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel" id="p-export" role="tabpanel" aria-labelledby="t-export" hidden>
|
|
<ul class="exports">
|
|
<li><button type="button" class="out" data-out="havre"><b>HAVRE CARD</b><span>.havre.json, the whole character</span></button></li>
|
|
<li><button type="button" class="out" data-out="v3"><b>CHARACTER CARD V3</b><span>.json for SillyTavern, RisuAI and other card apps</span></button></li>
|
|
<li><button type="button" class="out" data-out="png"><b>CHARACTER CARD PNG</b><span>a picture with the card inside it</span></button></li>
|
|
<li><button type="button" class="out" data-out="prompt"><b>SYSTEM PROMPT</b><span>.txt for any OpenAI-compatible API</span></button></li>
|
|
</ul>
|
|
<p class="mono">files are made here, in your browser. nothing is uploaded.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="row" id="how" aria-labelledby="how-h">
|
|
<h2 id="how-h">HOW IT WORKS</h2>
|
|
<div class="body">
|
|
<ol>
|
|
<li>Paste your Venice API key. It stays in this browser, and the page can reach api.venice.ai and nothing else.</li>
|
|
<li>Pick a character: one of Venice's, a card file you import, or one you write. Venice's characters say hello first. Or chat with a model and no character at all.</li>
|
|
<li>Talk. Replies come as a few short texts, through whichever Venice model you pick. Without a key, the characters you write answer from their own examples.</li>
|
|
<li>Keep them. Export a character as a file, or remix them into someone new; the credit stays in the card.</li>
|
|
</ol>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="row" id="agents" aria-labelledby="agents-h">
|
|
<h2 id="agents-h">FOR AGENTS</h2>
|
|
<div class="body">
|
|
<p>Copy this into any agent. It reads the skill, writes the character, and hands back the files. Change the second line to who you want.</p>
|
|
<pre id="agent-prompt" class="pre"></pre>
|
|
<p class="mono"><button type="button" class="btn copy" data-copy="agent-prompt">COPY PROMPT</button> files: <a href="skill.md">skill.md</a> / <a href="card.schema.json">card.schema.json</a> / <a href="card.js">card.js</a> / <a href="venice.js">venice.js</a> / <a href="llms.txt">llms.txt</a></p>
|
|
<p>What comes back: a Havre card (.havre.json) to import here, a Character Card V3 for SillyTavern, RisuAI and other card apps, and a plain system prompt for any OpenAI-compatible API.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="row" id="costs" aria-labelledby="costs-h">
|
|
<h2 id="costs-h">COSTS</h2>
|
|
<div class="body">
|
|
<p>You pay Venice directly, for what you use. Havre adds nothing. Rough prices, October 2026:</p>
|
|
<table class="prices">
|
|
<tbody>
|
|
<tr><th scope="row">a chat turn</th><td>$0.0007 to $0.005, by model (3,000 tokens in, 150 out)</td></tr>
|
|
<tr><th scope="row">a hello from a venice character</th><td>one chat turn</td></tr>
|
|
<tr><th scope="row">a remix</th><td>two short calls; five more for a venice character</td></tr>
|
|
</tbody>
|
|
</table>
|
|
<p class="mono">the model picker shows venice's price per million tokens, and your balance shows next to your key.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="row" id="plans" aria-labelledby="plans-h">
|
|
<h2 id="plans-h">PLANS</h2>
|
|
<div class="body">
|
|
<p><b>Studio, free.</b> Everything on this page, in your browser: as many characters as you like, import and export, remix with credit.</p>
|
|
<p><b>Harbor, planned at $8 a month for founding members.</b> Hosted character pages with a link of their own, an API and an MCP server, channels starting with Whop, and payouts to creators. Nothing is charged now. <a href="https://teahub.xyz/teahubxyz/time/issues/new?template=early-access.yml&title=Early+access%3A+Havre&venture=havre&plan=Harbor+%28founding%29">Join early access</a>.</p>
|
|
<p class="mono">early access is a public issue on teahub: no email, nothing charged.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="row" id="next" aria-labelledby="next-h">
|
|
<h2 id="next-h">COMING NEXT</h2>
|
|
<div class="body">
|
|
<ul>
|
|
<li>Photos: a character sends a picture of themselves, made from their card photo, with the price on it.</li>
|
|
<li>Voice notes: a voice for each character, with the words under it.</li>
|
|
<li>Hearing you and seeing your photos, if you switch them on.</li>
|
|
<li>Scenes: two characters take turns from your opening line.</li>
|
|
<li>A hosted harbor: a page and a link for every character, and an API and an MCP server.</li>
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="row" id="fine" aria-labelledby="fine-h">
|
|
<h2 id="fine-h">FINE PRINT</h2>
|
|
<div class="body">
|
|
<p>Characters and conversations are saved in this browser. Export them to keep them anywhere else.</p>
|
|
<p>Your key stays in this browser and goes only to api.venice.ai; the page's security policy blocks every other address. Conversations run on your Venice account, under Venice's terms.</p>
|
|
<p>Adult characters stay hidden unless you switch them on and confirm you're 18 or older.</p>
|
|
<p>Someone else's character comes in as a link with credit, not a copy, and Remix writes someone new from ideas alone. Names and broad ideas aren't protected by copyright; a character's written expression can be. When in doubt, ask the creator. This isn't legal advice.</p>
|
|
<p>Havre is independent. It isn't part of Venice.ai and isn't endorsed by Venice.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="row last" id="name" aria-labelledby="name-h">
|
|
<h2 id="name-h">THE NAME</h2>
|
|
<div class="body">
|
|
<p><i>Havre</i> means harbor. In July 2025 Venice and Le Havre signed a letter of friendship: two old port cities, both on the UNESCO World Heritage list.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<footer class="foot">havre.yolopreneur.lol / a venture on <a href="https://yolopreneur.lol/">yolopreneur.lol</a> / made by claude, october 2026 / not affiliated with venice.ai</footer>
|
|
|
|
<dialog id="importdlg" class="dlg" aria-labelledby="imp-h">
|
|
<form method="dialog" id="impform">
|
|
<h2 id="imp-h">IMPORT A CARD</h2>
|
|
<div class="dlgbody">
|
|
<div class="drop" id="drop" tabindex="0" role="button" aria-label="Choose a card file">
|
|
<b>DROP A CARD HERE</b><span>Havre JSON, or a character card V1, V2 or V3 (JSON or PNG)</span>
|
|
<span class="mono">or press to choose a file</span>
|
|
</div>
|
|
<input type="file" id="file" accept=".json,.png,application/json,image/png" hidden>
|
|
<label>OR PASTE A DESCRIPTION <textarea id="paste" rows="5" placeholder="Name on the first line, then who they are."></textarea></label>
|
|
<p id="impstatus" class="status" role="status"></p>
|
|
<p class="actions"><button type="button" class="btn" id="impcancel">CLOSE</button> <button type="button" class="btn solid" id="imp-paste">IMPORT TEXT</button></p>
|
|
</div>
|
|
</form>
|
|
</dialog>
|
|
|
|
<dialog id="modeldlg" class="dlg wide" aria-labelledby="model-h">
|
|
<form method="dialog" id="modelform">
|
|
<h2 id="model-h">MODEL</h2>
|
|
<div class="dlgbody">
|
|
<p class="mono" id="model-for"></p>
|
|
<label class="sr" for="msearch">Search models</label>
|
|
<input id="msearch" type="search" placeholder="search models: a name, private, uncensored…" autocomplete="off">
|
|
<div class="mtable">
|
|
<table class="table models">
|
|
<thead><tr><th scope="col">MODEL</th><th scope="col">PRIVACY</th><th scope="col">CAN</th><th scope="col">$ IN / OUT</th><th scope="col">CONTEXT</th></tr></thead>
|
|
<tbody id="mlist"></tbody>
|
|
</table>
|
|
</div>
|
|
<p class="mono">private: what you say is used for the reply and not kept. anonymized: venice passes it to the model's maker without saying who you are, and they can read it. prices are per million tokens.</p>
|
|
<p class="actions"><button type="button" class="btn" id="mclose">CLOSE</button></p>
|
|
</div>
|
|
</form>
|
|
</dialog>
|
|
|
|
<div id="toast" class="toast" role="status" aria-live="polite" hidden></div>
|
|
</body>
|
|
</html>
|