Files
protocol-droid/frontend/index.html
T
Protocolbot 4c05c1067e fix: YAML folded scalar parser, delete redirect, #protocols routing, forked_from null
- Fix YAML fallback parser to properly handle folded scalars (>-) at
  both top-level (description, outcome, practice) and step-level
  (step descriptions). Previously stored '>-' as the literal value
  instead of the folded text.

- Fix delete protocol/collection: reset currentRoute guard so
  navigate('library') actually works after deletion.

- Change #library hash to #protocols throughout (routing, nav links,
  breadcrumb shows 'protocols/' on the main page).

- Fix breadcrumb home click not working from detail pages (allow
  re-navigation to library even if currentRoute matches).

- Fix forked_from: null showing as a clickable link — now suppressed
  when value is null or the string 'null'.

- README: replace curl seed instructions with About page button,
  add About page customization instructions for whitelabeling.
2026-07-06 20:42:44 -06:00

237 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Protocol Droid</title>
<link rel="stylesheet" href="/frontend/style.css">
<meta name="theme-color" content="#0a1929">
</head>
<body>
<header>
<div class="breadcrumb" onclick="navigate('library')">
<span><span class="prompt">$</span> protocol-droid</span>
<span class="crumb-path" id="crumbPath"></span>
</div>
<nav id="navBar">
<a href="#protocols" class="active" data-view="library">PROTOCOLS</a>
<a href="#collections" data-view="collections">COLLECTIONS</a>
<a href="#about" data-view="about">ABOUT</a>
</nav>
<div class="auth-area" id="authArea"></div>
<button class="hamburger" id="hamburger" onclick="toggleMenu()">&#9776;</button>
</header>
<div class="mobile-panel" id="mobilePanel">
<a href="#protocols" data-view="library" class="active" onclick="mobileNav('library')">PROTOCOLS</a>
<a href="#collections" data-view="collections" onclick="mobileNav('collections')">COLLECTIONS</a>
<a href="#about" data-view="about" onclick="mobileNav('about')">ABOUT</a>
<div class="mobile-auth" id="mobileAuth"></div>
</div>
<main class="main">
<!-- LIBRARY VIEW -->
<section class="view" id="view-library" style="display:none">
<div class="toolbar">
<div class="search-bar">
<span class="prompt">&gt;</span>
<input type="text" id="searchInput" placeholder="search protocols..." oninput="debouncedLoadProtocols()">
</div>
<button class="btn create-btn" onclick="navigate('create')"><span class="create-text">+ Create</span><span class="create-plus">+</span></button>
</div>
<div class="sort-bar" id="sortBar">
<span class="sort-label">sort:</span>
<span class="sort-option active" onclick="setSort('date')">date added</span>
<span class="sort-option" onclick="setSort('relevance')">relevance</span>
<span class="filter-toggle" onclick="toggleFilterPanel()">&#9776; filter</span>
</div>
<div class="filter-panel" id="filterPanel">
<div class="search-bar" style="margin-bottom:10px">
<span class="prompt">&gt;</span>
<input type="text" id="tagSearch" placeholder="search tags..." oninput="renderFilteredTags()">
</div>
<div class="filters" id="filterBar"></div>
</div>
<div class="protocol-grid" id="protocolGrid"></div>
</section>
<!-- ABOUT VIEW -->
<section class="view" id="view-about" style="display:none">
<div class="detail-header">
<h1>About Protocol Droid</h1>
<p class="description">A tool for authoring, sharing, and curating social protocols.</p>
</div>
<div class="detail-section">
<p class="outcome">Protocol Droid is a project of the <a href="https://MEDLab.host" target="_blank">Media Economies Design Lab</a> at the University of Colorado Boulder. The name is inspired by the class of droids in Star Wars known as protocol droids, which help facilitate cross-cultural interactions.</p>
<p class="outcome" style="margin-top:12px">This project is inspired by RegenHub's <a href="https://library.regenhub.xyz/" target="_blank">Protocol Library</a>, developed by Kevin Owocki, and draws on the experience of MEDLab's <a href="https://communityrule.info" target="_blank">CommunityRule</a> platform.</p>
</div>
<div class="detail-section">
<h2>What is a protocol?</h2>
<p class="outcome">A protocol is a pattern of interaction among people, generally in the form of a particular sequence of actions. Protocols are micro-practices — more specific than patterns and more interaction-focused than rules. A rule says "decisions are made by consensus." A protocol says "here's the step-by-step process for reaching consensus in a meeting."</p>
</div>
<div class="detail-section">
<h2>Features</h2>
<p class="outcome">Browse a library of protocols contributed by the community. Author your own protocols with structured fields — steps, outcomes, practice tips. Fork existing protocols and adapt them to your context. Curate collections of protocols and share them. Tag protocols for discoverability. Export and import protocols as portable YAML files.</p>
</div>
<div class="detail-section">
<h2>Self-hosting</h2>
<p class="outcome">Protocol Droid is designed to be self-hostable and whitelabel-able. It runs on a standard LAMP stack with no external dependencies — no web fonts, no CDNs, no tracking. Deploy on Cloudron, any LAMP server, or your own infrastructure. Configure your site name, tagline, and theme through a simple config file.</p>
</div>
<div class="detail-section">
<h2>Sister project</h2>
<p class="outcome">Protocol Droid has a sister project: the <a href="https://git.medlab.host/ntnsndr/protocol-bicorder" target="_blank">Protocol Bicorder</a> — a diagnostic tool for evaluating protocols along a series of gradients between opposing terms. Where Protocol Droid is for documenting and sharing protocols, the Bicorder is for analyzing and comparing them.</p>
</div>
<div class="detail-section">
<h2>License</h2>
<p class="outcome">Hippocratic License (HL3-CORE) — do no harm. See <a href="https://firstdonoharm.dev/" target="_blank">firstdonoharm.dev</a>. Source code available on <a href="https://git.medlab.host/ntnsndr/protocol-droid" target="_blank">Gitea</a>.</p>
</div>
</section>
<!-- PROTOCOL DETAIL VIEW -->
<section class="view" id="view-detail" style="display:none">
<div id="detailContent"></div>
<div class="actions" id="detailActions"></div>
</section>
<!-- CREATE/EDIT PROTOCOL VIEW -->
<section class="view" id="view-create" style="display:none">
<form id="protocolForm" onsubmit="saveProtocol(event)">
<div class="form-group">
<label>Title</label>
<input type="text" name="title" required maxlength="255" placeholder="Round Robin Check-In">
</div>
<div class="form-group">
<label>Description</label>
<textarea name="description" rows="2" maxlength="280" placeholder="A sentence or two about its context and purpose"></textarea>
</div>
<div class="form-group">
<label>Tags</label>
<div class="tag-input-wrapper">
<div class="tag-chips" id="tagChips"></div>
<input type="text" id="tagInput" placeholder="type a tag and press enter..." onkeydown="handleTagKeydown(event)" oninput="showTagSuggestions()">
<div class="tag-suggestions" id="tagSuggestions"></div>
</div>
</div>
<div class="form-group">
<label>Source</label>
<input type="text" name="source" placeholder="Group Works Deck (or @username)">
</div>
<div class="form-group">
<label>Source URL</label>
<input type="text" name="source_url" placeholder="https://...">
</div>
<div id="stepsEditor"></div>
<button type="button" class="btn add-step" onclick="addStep()">+ Add Step</button>
<div class="form-group">
<label>Outcome</label>
<textarea name="outcome" rows="2" placeholder="What should happen if it is done right"></textarea>
</div>
<div class="form-group">
<label>Practice</label>
<textarea name="practice" rows="3" placeholder="Tips for learning to do it better"></textarea>
</div>
<div class="form-group">
<label>Visibility</label>
<select name="visibility">
<option value="public">Public — visible in the library</option>
<option value="unlisted">Unlisted — accessible by URL only</option>
<option value="private">Private — only visible to you</option>
</select>
</div>
<div class="actions">
<button type="submit" class="btn primary">Save Protocol</button>
<button type="button" class="btn" onclick="navigate('library')">Cancel</button>
</div>
</form>
</section>
<!-- COLLECTIONS VIEW -->
<section class="view" id="view-collections" style="display:none">
<div class="toolbar">
<div class="search-bar">
<span class="prompt">&gt;</span>
<input type="text" id="collectionSearch" placeholder="search collections..." oninput="loadCollections()">
</div>
<button class="btn create-btn" onclick="navigate('create-collection')"><span class="create-text">+ Create</span><span class="create-plus">+</span></button>
</div>
<div class="protocol-grid" id="collectionsGrid"></div>
</section>
<!-- CREATE/EDIT COLLECTION VIEW -->
<section class="view" id="view-create-collection" style="display:none">
<form id="collectionForm" onsubmit="saveCollection(event)">
<div class="form-group">
<label>Title</label>
<input type="text" name="coll-title" required maxlength="255" placeholder="Protocols for Mutual Aid Groups">
</div>
<div class="form-group">
<label>Description</label>
<textarea name="coll-description" rows="2" maxlength="280" placeholder="A sentence or two about the collection's context and purpose"></textarea>
</div>
<div class="form-group">
<label>Source</label>
<input type="text" name="coll-source" placeholder="Author or community (or @username)">
</div>
<div class="form-group">
<label>Visibility</label>
<select name="coll-visibility">
<option value="public">Public — visible in the library</option>
<option value="unlisted">Unlisted — accessible by URL only</option>
<option value="private">Private — only visible to you</option>
</select>
</div>
<div class="form-group">
<label>Add protocols to this collection</label>
<div id="collProtocolPicker"></div>
</div>
<div class="actions">
<button type="submit" class="btn primary">Save Collection</button>
<button type="button" class="btn" onclick="navigate('collections')">Cancel</button>
</div>
</form>
</section>
<!-- COLLECTION DETAIL VIEW -->
<section class="view" id="view-collection-detail" style="display:none">
<div id="collectionDetailContent"></div>
<div class="actions" id="collectionDetailActions"></div>
</section>
<!-- AUTH MODAL -->
<div class="modal-overlay" id="authModal" style="display:none">
<div class="modal">
<h2 id="authModalTitle">Sign In</h2>
<form onsubmit="handleAuth(event)">
<div id="authFormArea">
<div class="form-group">
<label>Username</label>
<input type="text" id="authUsername" required pattern="[a-zA-Z0-9_]{3,32}" placeholder="username">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" id="authPassword" required minlength="6" placeholder="••••••••">
</div>
<div class="form-group" id="displayNameGroup">
<label>Display Name (optional)</label>
<input type="text" id="authDisplayName" placeholder="Jane Doe">
</div>
</div>
<div class="actions">
<button type="submit" class="btn primary" id="authSubmit">Sign In</button>
<button type="button" class="btn" onclick="closeAuthModal()">Cancel</button>
</div>
<div class="auth-toggle" id="authToggle"></div>
</form>
</div>
</div>
<!-- TOAST -->
<div class="toast" id="toast" style="display:none"></div>
</main>
<script src="/frontend/app.js"></script>
</body>
</html>