From 3c06c95f396b6e911076bc3291d5855ed01b5caa Mon Sep 17 00:00:00 2001
From: "netop://ウィビ" <paul@webb.page>
Date: Sun, 26 Apr 2026 20:18:30 -0700
Subject: cleanup and ready for launch

---
 source/library/components/HistoryPanel.svelte | 225 ++++++++++++++------------
 1 file changed, 122 insertions(+), 103 deletions(-)

(limited to 'source/library/components/HistoryPanel.svelte')

diff --git a/source/library/components/HistoryPanel.svelte b/source/library/components/HistoryPanel.svelte
index 01f397a..e224e1e 100644
--- a/source/library/components/HistoryPanel.svelte
+++ b/source/library/components/HistoryPanel.svelte
@@ -1,4 +1,5 @@
 <script lang="ts">
+  /*** UTILITY ------------------------------------------ ***/
   import type { HistoryEntry } from "../state/history.svelte.ts";
 
   type Props = {
@@ -34,6 +35,7 @@
     return b.timestamp - a.timestamp;
   }));
 
+  /*** HELPER ------------------------------------------- ***/
   function formatTimestamp(ms: number): string {
     const d = new Date(ms);
     return d.toLocaleString();
@@ -50,10 +52,6 @@
     }
   }
 
-  function onImportClick() {
-    fileInput?.click();
-  }
-
   function onFileChange(event: Event) {
     const input = event.currentTarget as HTMLInputElement;
     const file = input.files?.[0];
@@ -63,22 +61,29 @@
 
     input.value = "";
   }
+
+  function onImportClick() {
+    fileInput?.click();
+  }
 </script>
 
 <style lang="scss">
   .panel {
-    background: var(--graphiql-panel, #252526);
-    border-right: 1px solid var(--graphiql-border, #333);
+    background-color: var(--uchu-yin-9);
+    color: var(--uchu-yin-1);
     display: grid;
+    font-size: 0.8rem;
     grid-template-rows: auto 1fr;
-    height: 100%;
     min-height: 0;
+    height: 100%;
     overflow: hidden;
+    z-index: 2;
   }
 
-  .header {
+  .header,
+  .notice {
     align-items: center;
-    border-bottom: 1px solid var(--graphiql-border, #333);
+    border-bottom: 1px solid var(--uchu-yin-8);
     display: flex;
     gap: 0.5rem;
     justify-content: space-between;
@@ -86,26 +91,32 @@
   }
 
   .title {
-    font-size: 0.8125rem;
     font-weight: 600;
   }
 
   .actions {
     display: flex;
     gap: 0.5rem;
+
+    .action {
+      background: none;
+      border: none;
+      cursor: pointer;
+      font-family: inherit;
+      font-size: 0.75rem;
+      padding: 0;
+    }
   }
 
-  .action {
-    background: none;
-    border: none;
-    color: var(--graphiql-muted, #858585);
-    cursor: pointer;
-    font-family: inherit;
-    font-size: 0.75rem;
-    padding: 0;
+  .action,
+  .notice-dismiss,
+  .remove {
+    &:not(:hover) {
+      color: var(--uchu-yin-5);
+    }
 
     &:hover {
-      color: var(--graphiql-fg, #d4d4d4);
+      color: var(--uchu-yin-1);
     }
   }
 
@@ -113,38 +124,20 @@
     display: none;
   }
 
-  .notice {
-    align-items: center;
-    background: var(--graphiql-bg, #1e1e1e);
-    border-bottom: 1px solid var(--graphiql-border, #333);
-    color: var(--graphiql-fg, #d4d4d4);
-    display: flex;
-    font-size: 0.75rem;
-    gap: 0.5rem;
-    justify-content: space-between;
-    padding: 0.375rem 0.75rem;
-  }
-
-  .notice-dismiss {
+  .notice-dismiss,
+  .remove {
     background: none;
     border: none;
-    color: var(--graphiql-muted, #858585);
     cursor: pointer;
-    font-size: 0.875rem;
+    font-size: 1rem;
     line-height: 1;
     padding: 0 0.25rem;
-
-    &:hover {
-      color: var(--graphiql-fg, #d4d4d4);
-    }
   }
 
   .list {
     display: grid;
-    gap: 0.125rem;
     min-height: 0;
     overflow-y: auto;
-    padding: 0.375rem 0;
   }
 
   .entry {
@@ -154,22 +147,56 @@
     gap: 0.125rem;
     grid-template-columns: auto 1fr auto;
     padding: 0.375rem 0.75rem;
+    transition: opacity 0.1s;
+
+    &:not(:last-of-type) {
+      border-bottom: 1px solid var(--uchu-yin);
+    }
 
     &:hover {
-      background: var(--graphiql-bg, #1e1e1e);
+      background-color: var(--uchu-yin);
+    }
+
+    .list:has(.entry:hover) &:not(:hover) {
+      opacity: 0.4;
+    }
+
+    &-title {
+      font-size: 0.8rem;
+      margin-bottom: 0.1rem;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+
+    &-time {
+      color: var(--uchu-yin-5);
+      font-size: 0.7rem;
     }
   }
 
   .star {
+    align-self: center;
     background: none;
     border: none;
-    color: var(--graphiql-muted, #858585);
     cursor: pointer;
-    font-size: 0.875rem;
+    font-size: 0.8rem;
     padding: 0 0.375rem 0 0;
 
+    &:not(.active) {
+      color: var(--uchu-yin-5);
+    }
+
     &.active {
-      color: var(--graphiql-accent, #e3b341);
+      color: var(--uchu-orange-4);
+
+      svg {
+        fill: currentColor;
+      }
+    }
+
+    svg {
+      width: 1rem;
     }
   }
 
@@ -178,75 +205,55 @@
     min-width: 0;
   }
 
-  .entry-title {
-    font-size: 0.8125rem;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-  }
-
-  .entry-time {
-    color: var(--graphiql-muted, #858585);
-    font-size: 0.7rem;
-  }
-
   .remove {
     align-self: center;
-    background: none;
-    border: none;
-    color: var(--graphiql-muted, #858585);
-    cursor: pointer;
-    font-size: 1rem;
-    line-height: 1;
-    padding: 0 0.25rem;
-
-    &:hover {
-      color: var(--graphiql-fg, #d4d4d4);
-    }
   }
 
   .empty {
-    color: var(--graphiql-muted, #858585);
-    font-size: 0.8125rem;
+    color: var(--uchu-yin-5);
+    font-size: 0.8rem;
     padding: 0.75rem;
   }
 </style>
 
 <div class="panel">
-  <div>
-    <div class="header">
-      <span class="title">History</span>
-      <div class="actions">
-        {#if onExport}
-          <button class="action" onclick={onExport} type="button">Export</button>
-        {/if}
-        {#if onImport}
-          <button class="action" onclick={onImportClick} type="button">Import</button>
-          <input
-            accept="application/json"
-            bind:this={fileInput}
-            class="hidden-input"
-            onchange={onFileChange}
-            type="file"/>
-        {/if}
-        {#if entries.length > 0}
-          <button class="action" onclick={onClear} type="button">Clear</button>
-        {/if}
-      </div>
+  <div class="header">
+    <span class="title">History</span>
+
+    <div class="actions">
+      {#if onExport}
+        <button class="action" onclick={onExport} type="button">Export</button>
+      {/if}
+
+      {#if onImport}
+        <button class="action" onclick={onImportClick} type="button">Import</button>
+        <input
+          accept="application/json"
+          bind:this={fileInput}
+          class="hidden-input"
+          onchange={onFileChange}
+          type="file"/>
+      {/if}
+
+      {#if entries.length > 0}
+        <button class="action" onclick={onClear} type="button">Clear</button>
+      {/if}
     </div>
-    {#if notice}
-      <div class="notice">
-        <span>{notice}</span>
-        {#if onDismissNotice}
-          <button
-            aria-label="Dismiss notice"
-            class="notice-dismiss"
-            onclick={onDismissNotice}
-            type="button">×</button>
-        {/if}
-      </div>
-    {/if}
   </div>
+
+  {#if notice}
+    <div class="notice">
+      <span>{notice}</span>
+      {#if onDismissNotice}
+        <button
+          aria-label="Dismiss notice"
+          class="notice-dismiss"
+          onclick={onDismissNotice}
+          type="button">&times;</button>
+      {/if}
+    </div>
+  {/if}
+
   <div class="list">
     {#if sorted.length === 0}
       <div class="empty">No history yet.</div>
@@ -264,16 +271,28 @@
             class="star"
             class:active={entry.favorite}
             onclick={(e) => { e.stopPropagation(); onFavorite(entry.id); }}
-            type="button">{entry.favorite ? "★" : "☆"}</button>
+            type="button">
+            {#if entry.favorite}
+              <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+                <path d="M12 2L14.9389 7.95492L21.5106 8.90983L16.7553 13.5451L17.8779 20.0902L12 17L6.12215 20.0902L7.24472 13.5451L2.48944 8.90983L9.06108 7.95492L12 2Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
+              </svg>
+            {:else}
+              <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
+                <path d="M12 2L14.9389 7.95492L21.5106 8.90983L16.7553 13.5451L17.8779 20.0902L12 17L6.12215 20.0902L7.24472 13.5451L2.48944 8.90983L9.06108 7.95492L12 2Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
+              </svg>
+            {/if}
+          </button>
+
           <div class="meta">
             <div class="entry-title">{entry.title}</div>
             <div class="entry-time">{formatTimestamp(entry.timestamp)}</div>
           </div>
+
           <button
             aria-label="Remove entry"
             class="remove"
             onclick={(e) => { e.stopPropagation(); onRemove(entry.id); }}
-            type="button">×</button>
+            type="button">&times;</button>
         </div>
       {/each}
     {/if}
-- 
cgit v1.2.3

