view mrjunejune/src/jrpg/index.html @ 273:e02e2036ef84 default tip

add Layer 2 JRPG component system Add reusable content and window modals, an isolated component sandbox, shared cyberpunk scroll areas, production-safe cache freshness, and server-rendered JRPG panel state. Co-authored-by: Copilot <[email protected]>
author MrJuneJune <me@mrjunejune.com>
date Sat, 08 Aug 2026 02:08:08 -0700
parents 41a49c29a28f
children
line wrap: on
line source

<!doctype html>
<html lang="en" data-zen-theme="cyberpunk">
  <head>
    <title>Shiba Quest | MrJuneJune</title>
    {{/parts/base_head.html}}
    <link rel="preload" href="/public/jrpg/background-frame-2.webp" as="image" media="not ((max-width: 52rem) and (orientation: portrait))">
    <link rel="preload" href="/public/jrpg/background-frame-mobile.webp" as="image" media="(max-width: 52rem) and (orientation: portrait)">
    <link rel="preload" href="/public/jrpg/bar-ink.webp" as="image">
    <link rel="stylesheet" href="/public/mjj-composer.css">
    <link rel="stylesheet" href="/public/mjj-modal.css">
    <link rel="stylesheet" href="/jrpg/jrpg.css">
    <script type="module" src="/public/mjj-composer.js"></script>
    <script type="module" src="/public/mjj-modal.js"></script>
    <script type="module" src="/jrpg/jrpg.js"></script>
  </head>
  <body class="jrpg-page">
    <main class="jrpg-main">
      <mjj-jrpg-shell
        class="jrpg-shell"
        data-initial-panel="__MJJ_PANEL__"
      >
        <div class="jrpg-workspace">
          <div class="jrpg-brand">
            <header>
              <h1>
                <a href="/" data-zen-link-effect="none">MrJuneJune</a>
              </h1>
            </header>
          </div>
          <nav class="jrpg-frame-controls" aria-label="Window controls">
            <zen-button appearance="plain" size="xs">
              <button
                type="button"
                data-frame-minimize
                aria-label="Minimize interface"
                aria-pressed="false"
              >
                <zen-icon name="arrow-down"></zen-icon>
              </button>
            </zen-button>
            <zen-button appearance="plain" size="xs">
              <button
                type="button"
                data-frame-fullscreen
                aria-label="Toggle fullscreen"
                aria-pressed="false"
              >
                <zen-icon name="external"></zen-icon>
              </button>
            </zen-button>
            <zen-button appearance="plain" size="xs">
              <a href="/" aria-label="Close interface and return home">
                <zen-icon name="close"></zen-icon>
              </a>
            </zen-button>
          </nav>
          <mjj-window-modal
            class="jrpg-mobile-destination-owner"
            data-mobile-destination-owner
          >
            <zen-dialog>
              <zen-button class="jrpg-mobile-menu-btn" appearance="plain" size="xs">
                <button
                  type="button"
                  data-zen-trigger
                  data-mobile-menu-toggle
                  aria-label="Open destination menu"
                >
                  <zen-icon name="menu"></zen-icon>
                </button>
              </zen-button>
              <dialog
                class="jrpg-mobile-destination-dialog"
                data-mobile-destination-dialog
              >
                <header class="jrpg-mobile-destination-header" data-modal-header>
                  <zen-button
                    appearance="plain"
                    size="sm"
                    data-mobile-destination-back-owner
                    hidden
                  >
                    <button
                      type="button"
                      data-mobile-destination-back
                      aria-label="Back to destination menu"
                    >
                      <zen-icon name="chevron-left"></zen-icon>
                      Menu
                    </button>
                  </zen-button>
                  <h2
                    id="jrpg-mobile-destination-title"
                    data-mobile-destination-title
                    data-modal-title
                  >Menu</h2>
                  <zen-button appearance="plain" size="sm">
                    <button
                      type="button"
                      data-zen-close
                      aria-label="Close destination menu"
                    >
                      <zen-icon name="close"></zen-icon>
                    </button>
                  </zen-button>
                </header>
                <zen-scroll-area
                  class="jrpg-mobile-destination-content"
                  data-mobile-destination-content
                  data-modal-body
                  data-cyber-scroll
                  tabindex="0"
                  aria-label="Destination content"
                ></zen-scroll-area>
              </dialog>
            </zen-dialog>
          </mjj-window-modal>
          <footer class="jrpg-frame-telemetry" aria-label="System telemetry">
            <span aria-label="Temperature unavailable">N/A</span>
            <span data-frame-account aria-live="polite"><a href="/login?next=/jrpg">LOGIN</a></span>
            <span data-frame-network aria-label="Status online">ONLINE</span>
            <time data-frame-uptime aria-label="Uptime">00:00:00</time>
          </footer>

          <section class="jrpg-scene" aria-label="Shiba Quest conversation">
            <mjj-jrpg-character state="default">
              <img data-character alt="">
            </mjj-jrpg-character>

            <mjj-jrpg-chat aria-label="Conversation">
              <zen-message-scroller>
                <div
                  class="jrpg-message-viewport"
                  data-zen-viewport
                  role="log"
                  aria-live="polite"
                  aria-relevant="additions text"
                >
                  <ol class="jrpg-messages" data-messages></ol>
                </div>
              </zen-message-scroller>
              <nav class="jrpg-turn-navigation" aria-label="Conversation turns">
                <zen-button appearance="plain" size="sm">
                  <button type="button" data-turn-previous aria-label="Previous turn" disabled>
                    <zen-icon name="chevron-left"></zen-icon>
                    Previous
                  </button>
                </zen-button>
                <span data-turn-position aria-live="polite">START</span>
                <zen-button appearance="plain" size="sm">
                  <button type="button" data-turn-next aria-label="Next turn" disabled>
                    Next
                    <zen-icon name="chevron-right"></zen-icon>
                  </button>
                </zen-button>
              </nav>
            </mjj-jrpg-chat>
          </section>

          <section class="jrpg-utility" aria-label="Selected destination">
            <mjj-jrpg-preview data-selection="__MJJ_PREVIEW_SELECTION__">
              <article class="jrpg-preview-panel" aria-live="polite">
                <div
                  class="jrpg-preview-content"
                  data-preview-content
                  __MJJ_PREVIEW_HIDDEN__
                >
                  <zen-heading size="xl">
                    <h2 data-preview-title>__MJJ_PREVIEW_TITLE__</h2>
                  </zen-heading>
                  <p data-preview-copy>
                    __MJJ_PREVIEW_COPY__
                  </p>
                  <ul class="jrpg-work-showcase" data-work-showcase>__MJJ_PREVIEW_WORKS__</ul>
                  <mjj-content-modal data-detail-modal-owner>
                    <zen-dialog class="jrpg-preview-dialog">
                      <zen-button appearance="plain" size="xs" data-dialog-trigger-owner hidden>
                        <button type="button" data-zen-trigger tabindex="-1" aria-hidden="true"></button>
                      </zen-button>
                      <dialog class="jrpg-detail-dialog">
                        <header class="jrpg-dialog-heading" data-modal-header>
                          <div data-modal-heading>
                            <p data-modal-kicker>DESTINATION DATA</p>
                            <h2
                              id="jrpg-dialog-title"
                              data-dialog-title
                              data-modal-title
                            >Resume</h2>
                          </div>
                        <div class="jrpg-dialog-heading-actions">
                          <zen-button
                            appearance="plain"
                            size="sm"
                            data-resume-download
                          >
                            <a href="/public/resume.pdf">
                              Download PDF
                              <zen-icon name="download"></zen-icon>
                            </a>
                          </zen-button>
                          <zen-button appearance="plain" size="sm">
                            <button type="button" data-zen-close aria-label="Close destination details">
                              <zen-icon name="close"></zen-icon>
                            </button>
                          </zen-button>
                          </div>
                        </header>
                        <zen-scroll-area
                          class="jrpg-detail-body"
                          data-modal-body
                          data-cyber-scroll
                          tabindex="0"
                          aria-label="Destination details"
                        >
                          <div class="jrpg-detail-content" data-modal-article>
                            <p data-dialog-copy>Experience, projects, and the systems I have helped build.</p>
                      <section
                        class="jrpg-resume-dossier"
                        data-resume-dossier
                        aria-label="Resume dossier"
                      >
                        <p data-resume-status>Loading resume dossier...</p>
                        <div data-resume-content></div>
                      </section>
                      <section
                        class="jrpg-blog-browser"
                        data-blog-browser
                        aria-label="Blog archive"
                        hidden
                      >
                        <aside>
                          <h3>All blogs</h3>
                          <ol data-blog-list>
                            <li>Loading blog archive...</li>
                          </ol>
                        </aside>
                        <article>
                          <p data-blog-status>Select a blog to inspect.</p>
                          <div data-blog-content></div>
                        </article>
                      </section>
                      <section
                        class="jrpg-tool-browser"
                        data-tool-browser
                        aria-label="Tool workspace"
                        hidden
                      >
                        <article>
                          <p data-tool-status>Select a tool to inspect.</p>
                          <div data-tool-content></div>
                        </article>
                      </section>
                          </div>
                        </zen-scroll-area>
                      </dialog>
                    </zen-dialog>
                  </mjj-content-modal>
                </div>
                <mjj-conversation-archive
                  __MJJ_ARCHIVE_HIDDEN__
                  aria-label="Conversations"
                >
              <div class="jrpg-archive-heading">
                <h2>Archive</h2>
                <zen-button appearance="plain" size="xs">
                  <button type="button" data-archive-close aria-label="Close conversations">
                    <zen-icon name="close"></zen-icon>
                  </button>
                </zen-button>
              </div>
              <div class="jrpg-archive-actions">
                <zen-button appearance="plain" size="sm">
                  <button type="button" data-archive-new>
                    <zen-icon name="plus"></zen-icon>
                    New
                  </button>
                </zen-button>
              </div>
              <ol class="jrpg-archive-list" data-archive-list aria-label="Conversations">
              </ol>
              <zen-button appearance="plain" size="sm" data-archive-load-more-owner hidden>
                <button type="button" data-archive-load-more>Load more</button>
              </zen-button>
              <p class="jrpg-archive-status" data-archive-status aria-live="polite">Loading conversations...</p>
              <mjj-content-modal data-archive-delete-owner>
                <zen-dialog>
                  <zen-button appearance="plain" size="xs" hidden>
                    <button type="button" data-zen-trigger tabindex="-1" aria-hidden="true"></button>
                  </zen-button>
                  <dialog
                    class="jrpg-archive-dialog"
                    data-archive-delete-dialog
                  >
                    <header data-modal-header>
                    <h2
                      id="archive-delete-title"
                      class="jrpg-archive-dialog-title"
                      data-modal-title
                    >Delete this conversation?</h2>
                    </header>
                    <div data-modal-body>
                    <div data-modal-article>
                      <p data-archive-delete-name class="jrpg-archive-dialog-subtitle"></p>
                    </div>
                    </div>
                    <footer class="jrpg-archive-dialog-actions" data-modal-footer>
                    <zen-button appearance="plain" size="md">
                      <button type="button" data-archive-delete-cancel>Cancel</button>
                    </zen-button>
                    <zen-button appearance="plain" size="md">
                      <button type="button" data-archive-delete-confirm>Delete</button>
                    </zen-button>
                    </footer>
                  </dialog>
                </zen-dialog>
              </mjj-content-modal>
              <mjj-window-modal data-archive-rename-owner>
                <zen-dialog>
                  <zen-button appearance="plain" size="xs" hidden>
                    <button type="button" data-zen-trigger tabindex="-1" aria-hidden="true"></button>
                  </zen-button>
                  <dialog
                    class="jrpg-archive-dialog"
                    data-archive-rename-dialog
                  >
                    <header data-modal-header>
                    <h2
                      id="archive-rename-title"
                      class="jrpg-archive-dialog-title"
                      data-modal-title
                    >Rename conversation</h2>
                    </header>
                    <form
                    id="archive-rename-form"
                    data-modal-body
                    data-archive-rename-form
                    novalidate
                    >
                    <zen-field appearance="plain" size="md">
                      <label for="archive-rename-input">New title</label>
                      <input
                        id="archive-rename-input"
                        data-archive-rename-input
                        type="text"
                        maxlength="200"
                        required
                        autocomplete="off"
                      >
                    </zen-field>
                    </form>
                    <footer class="jrpg-archive-dialog-actions" data-modal-footer>
                    <zen-button appearance="plain" size="md">
                      <button type="button" data-archive-rename-cancel>Cancel</button>
                    </zen-button>
                    <zen-button appearance="plain" size="md">
                      <button type="submit" form="archive-rename-form">Save</button>
                    </zen-button>
                    </footer>
                  </dialog>
                </zen-dialog>
              </mjj-window-modal>
                </mjj-conversation-archive>
              </article>
            </mjj-jrpg-preview>
          </section>

          <mjj-composer compact>
            <form class="jrpg-composer" data-composer>
              <div data-composer-control>
                <zen-field size="lg">
                  <label for="jrpg-message" data-composer-label>Message</label>
                  <textarea
                    id="jrpg-message"
                    name="message"
                    rows="3"
                    maxlength="500"
                    placeholder="Ask Epi about this site..."
                    required
                  ></textarea>
                </zen-field>
                <div class="jrpg-composer-actions" data-composer-actions>
                  <zen-button size="sm">
                    <button type="submit" aria-label="Send message">
                      <span data-composer-action-label>Send</span>
                      <zen-icon name="arrow-right"></zen-icon>
                    </button>
                  </zen-button>
                  <zen-button
                    size="sm"
                    variant="danger"
                    data-composer-cancel-owner
                    hidden
                  >
                    <button
                      type="button"
                      data-composer-cancel
                      aria-label="Cancel response"
                    >
                      <span data-composer-action-label>Cancel</span>
                      <zen-icon name="close"></zen-icon>
                    </button>
                  </zen-button>
                </div>
              </div>
              <p
                class="jrpg-composer-quota"
                data-composer-meta
                data-quota
                aria-live="polite"
                hidden
              ></p>
              <p
                class="jrpg-composer-hint"
                data-composer-meta
              >Enter to send / Shift + Enter for a new line</p>
            </form>
          </mjj-composer>

          <mjj-jrpg-menu id="jrpg-destination-menu">
            <nav aria-label="Shiba Quest destinations">
              <p class="jrpg-menu-label">Menu</p>
              <ul>
                <li>
                  <zen-button appearance="plain" size="sm">
                    <button type="button" data-preview="resume" aria-pressed="__MJJ_RESUME_PRESSED__">
                      <zen-icon name="chevron-right"></zen-icon>
                      Resume
                    </button>
                  </zen-button>
                </li>
                <li>
                  <zen-button appearance="plain" size="sm">
                    <button type="button" data-preview="tools" aria-pressed="__MJJ_TOOLS_PRESSED__">
                      <zen-icon name="chevron-right"></zen-icon>
                      Tools
                    </button>
                  </zen-button>
                </li>
                <li>
                  <zen-button appearance="plain" size="sm">
                    <button type="button" data-preview="blog" aria-pressed="__MJJ_BLOG_PRESSED__">
                      <zen-icon name="chevron-right"></zen-icon>
                      Blogs
                    </button>
                  </zen-button>
                </li>
                <li>
                  <zen-button appearance="plain" size="sm">
                    <button type="button" data-preview="conversations" aria-pressed="__MJJ_CONVERSATIONS_PRESSED__">
                      <zen-icon name="chevron-right"></zen-icon>
                      Conversations
                    </button>
                  </zen-button>
                </li>
              </ul>
            </nav>
          </mjj-jrpg-menu>

          <mjj-window-modal class="jrpg-login-dialog-owner" data-login-dialog-owner>
            <zen-dialog>
              <zen-button appearance="plain" size="md" hidden>
                <button type="button" data-zen-trigger tabindex="-1" aria-hidden="true"></button>
              </zen-button>
              <dialog
                class="jrpg-login-dialog"
                data-login-dialog
              >
                <header class="jrpg-login-dialog-header" data-modal-header>
                  <h2 id="jrpg-login-title" data-modal-title>SIGN IN</h2>
                  <zen-button appearance="plain" size="md">
                    <button type="button" data-zen-close aria-label="Close sign-in dialog">
                      <zen-icon name="close"></zen-icon>
                    </button>
                  </zen-button>
                </header>
                <form
                  id="jrpg-login-form"
                  data-login-form
                  data-modal-body
                  novalidate
                >
                  <div role="alert" data-login-error aria-live="assertive" hidden></div>
                <zen-field appearance="plain" size="md">
                  <label for="jrpg-login-username">Username</label>
                  <input
                    id="jrpg-login-username"
                    name="username"
                    type="text"
                    autocomplete="username"
                    autocapitalize="none"
                    spellcheck="false"
                    required
                    maxlength="32"
                  >
                </zen-field>
                <zen-field appearance="plain" size="md">
                  <label for="jrpg-login-password">Password</label>
                  <input
                    id="jrpg-login-password"
                    name="password"
                    type="password"
                    autocomplete="current-password"
                    required
                    minlength="12"
                    maxlength="1024"
                  >
                </zen-field>
                </form>
                <footer class="jrpg-login-dialog-actions" data-modal-footer>
                  <zen-button appearance="plain" size="md">
                    <button type="button" data-login-cancel data-zen-close>Cancel</button>
                  </zen-button>
                  <zen-button appearance="plain" size="md">
                    <button
                      type="submit"
                      form="jrpg-login-form"
                      data-login-submit
                    >Sign in</button>
                  </zen-button>
                </footer>
              </dialog>
            </zen-dialog>
          </mjj-window-modal>
        </div>
      </mjj-jrpg-shell>
    </main>
  </body>
</html>