Browse Source

share collapse system prompt

Jay V 8 months ago
parent
commit
484c90ed00
2 changed files with 116 additions and 57 deletions
  1. 80 42
      packages/web/src/components/Share.tsx
  2. 36 15
      packages/web/src/components/share.module.css

+ 80 - 42
packages/web/src/components/Share.tsx

@@ -399,21 +399,12 @@ export default function Share(props: { api: string }) {
     })
     })
   })
   })
 
 
-  const models = createMemo(() => {
-    const result: string[][] = []
-    for (const msg of messages()) {
-      if (msg.role === "assistant" && msg.metadata?.assistant) {
-        result.push([
-          msg.metadata.assistant.providerID,
-          msg.metadata.assistant.modelID,
-        ])
-      }
-    }
-    return result
-  })
-
-  const metrics = createMemo(() => {
+  const data = createMemo(() => {
     const result = {
     const result = {
+      created: undefined as number | undefined,
+      system: [] as string[],
+      messages: [] as SessionMessage[],
+      models: [] as string[][],
       cost: 0,
       cost: 0,
       tokens: {
       tokens: {
         input: 0,
         input: 0,
@@ -421,16 +412,39 @@ export default function Share(props: { api: string }) {
         reasoning: 0,
         reasoning: 0,
       },
       },
     }
     }
-    for (const msg of messages()) {
+    for (let i = 0; i < messages().length; i++) {
+      const msg = messages()[i]
+
+      const system = i === 0 && msg.role === "system"
       const assistant = msg.metadata?.assistant
       const assistant = msg.metadata?.assistant
-      if (!assistant) continue
-      result.cost += assistant.cost
-      result.tokens.input += assistant.tokens.input
-      result.tokens.output += assistant.tokens.output
-      result.tokens.reasoning += assistant.tokens.reasoning
+
+      if (system) {
+        for (const part of msg.parts) {
+          if (part.type === "text") {
+            result.system.push(part.text)
+          }
+        }
+        result.created = msg.metadata?.time.created
+        continue
+      }
+
+      result.messages.push(msg)
+
+      if (assistant) {
+        result.cost += assistant.cost
+        result.tokens.input += assistant.tokens.input
+        result.tokens.output += assistant.tokens.output
+        result.tokens.reasoning += assistant.tokens.reasoning
+
+        result.models.push([
+          assistant.providerID,
+          assistant.modelID,
+        ])
+      }
     }
     }
     return result
     return result
   })
   })
+  const [showingSystemPrompt, showSystemPrompt] = createSignal(false)
 
 
   return (
   return (
     <main class={`${styles.root} not-content`}>
     <main class={`${styles.root} not-content`}>
@@ -439,14 +453,14 @@ export default function Share(props: { api: string }) {
           <h1>{store.info?.title}</h1>
           <h1>{store.info?.title}</h1>
           <div>
           <div>
             <div data-section="date">
             <div data-section="date">
-              {messages().length > 0 && messages()[0].metadata?.time.created ? (
+              {data().created ? (
                 <span
                 <span
                   title={DateTime.fromMillis(
                   title={DateTime.fromMillis(
-                    messages()[0].metadata?.time.created || 0,
+                    data().created || 0,
                   ).toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)}
                   ).toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)}
                 >
                 >
                   {DateTime.fromMillis(
                   {DateTime.fromMillis(
-                    messages()[0].metadata?.time.created || 0,
+                    data().created || 0,
                   ).toLocaleString(DateTime.DATE_MED)}
                   ).toLocaleString(DateTime.DATE_MED)}
                 </span>
                 </span>
               ) : (
               ) : (
@@ -465,40 +479,40 @@ export default function Share(props: { api: string }) {
           <ul data-section="stats">
           <ul data-section="stats">
             <li>
             <li>
               <span data-element-label>Cost</span>
               <span data-element-label>Cost</span>
-              {metrics().cost !== undefined ? (
-                <span>${metrics().cost.toFixed(2)}</span>
+              {data().cost !== undefined ? (
+                <span>${data().cost.toFixed(2)}</span>
               ) : (
               ) : (
                 <span data-placeholder>&mdash;</span>
                 <span data-placeholder>&mdash;</span>
               )}
               )}
             </li>
             </li>
             <li>
             <li>
               <span data-element-label>Input Tokens</span>
               <span data-element-label>Input Tokens</span>
-              {metrics().tokens.input ? (
-                <span>{metrics().tokens.input}</span>
+              {data().tokens.input ? (
+                <span>{data().tokens.input}</span>
               ) : (
               ) : (
                 <span data-placeholder>&mdash;</span>
                 <span data-placeholder>&mdash;</span>
               )}
               )}
             </li>
             </li>
             <li>
             <li>
               <span data-element-label>Output Tokens</span>
               <span data-element-label>Output Tokens</span>
-              {metrics().tokens.output ? (
-                <span>{metrics().tokens.output}</span>
+              {data().tokens.output ? (
+                <span>{data().tokens.output}</span>
               ) : (
               ) : (
                 <span data-placeholder>&mdash;</span>
                 <span data-placeholder>&mdash;</span>
               )}
               )}
             </li>
             </li>
             <li>
             <li>
               <span data-element-label>Reasoning Tokens</span>
               <span data-element-label>Reasoning Tokens</span>
-              {metrics().tokens.reasoning ? (
-                <span>{metrics().tokens.reasoning}</span>
+              {data().tokens.reasoning ? (
+                <span>{data().tokens.reasoning}</span>
               ) : (
               ) : (
                 <span data-placeholder>&mdash;</span>
                 <span data-placeholder>&mdash;</span>
               )}
               )}
             </li>
             </li>
           </ul>
           </ul>
           <ul data-section="stats" data-section-models>
           <ul data-section="stats" data-section-models>
-            {models().length > 0 ? (
-              <For each={Array.from(models())}>
+            {data().models.length > 0 ? (
+              <For each={Array.from(data().models)}>
                 {([provider, model]) => (
                 {([provider, model]) => (
                   <li>
                   <li>
                     <div data-stat-model-icon title={provider}>
                     <div data-stat-model-icon title={provider}>
@@ -515,16 +529,44 @@ export default function Share(props: { api: string }) {
               </li>
               </li>
             )}
             )}
           </ul>
           </ul>
+          <div data-section="system-prompt">
+            <div data-section="icon">
+              <IconCpuChip width={16} height={16} />
+            </div>
+            <div data-section="content">
+              <button
+                type="button"
+                data-element-button-text
+                data-element-button-more
+                onClick={() => showSystemPrompt((e) => !e)}
+              >
+                <span>
+                  {showingSystemPrompt() ? "Hide system prompt" : "Show system prompt"}
+                </span>
+                <span data-button-icon>
+                  <Show
+                    when={showingSystemPrompt()}
+                    fallback={<IconChevronRight width={12} height={12} />}
+                  >
+                    <IconChevronDown width={12} height={12} />
+                  </Show>
+                </span>
+              </button>
+              <Show when={showingSystemPrompt()}>
+                <TextPart data-size="sm" expand text={data().system.join("\n")} />
+              </Show>
+            </div>
+          </div>
         </div>
         </div>
       </div>
       </div>
 
 
       <div>
       <div>
         <Show
         <Show
-          when={messages().length > 0}
+          when={data().messages.length > 0}
           fallback={<p>Waiting for messages...</p>}
           fallback={<p>Waiting for messages...</p>}
         >
         >
           <div class={styles.parts}>
           <div class={styles.parts}>
-            <For each={messages()}>
+            <For each={data().messages}>
               {(msg, msgIndex) => (
               {(msg, msgIndex) => (
                 <For each={msg.parts}>
                 <For each={msg.parts}>
                   {(part, partIndex) => {
                   {(part, partIndex) => {
@@ -537,13 +579,9 @@ export default function Share(props: { api: string }) {
                     const [results, showResults] = createSignal(false)
                     const [results, showResults] = createSignal(false)
                     const isLastPart = createMemo(
                     const isLastPart = createMemo(
                       () =>
                       () =>
-                        messages().length === msgIndex() + 1 &&
+                        data().messages.length === msgIndex() + 1 &&
                         msg.parts.length === partIndex() + 1,
                         msg.parts.length === partIndex() + 1,
                     )
                     )
-                    const time =
-                      msg.metadata?.time.completed ||
-                      msg.metadata?.time.created ||
-                      0
                     return (
                     return (
                       <Switch>
                       <Switch>
                         {/* User text */}
                         {/* User text */}
@@ -918,11 +956,11 @@ export default function Share(props: { api: string }) {
           }}
           }}
         >
         >
           <Show
           <Show
-            when={messages().length > 0}
+            when={data().messages.length > 0}
             fallback={<p>Waiting for messages...</p>}
             fallback={<p>Waiting for messages...</p>}
           >
           >
             <ul style={{ "list-style-type": "none", padding: 0 }}>
             <ul style={{ "list-style-type": "none", padding: 0 }}>
-              <For each={messages()}>
+              <For each={data().messages}>
                 {(msg) => (
                 {(msg) => (
                   <li
                   <li
                     style={{
                     style={{

+ 36 - 15
packages/web/src/components/share.module.css

@@ -8,19 +8,6 @@
   --term-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2060%2016'%20preserveAspectRatio%3D'xMidYMid%20meet'%3E%3Ccircle%20cx%3D'8'%20cy%3D'8'%20r%3D'8'%2F%3E%3Ccircle%20cx%3D'30'%20cy%3D'8'%20r%3D'8'%2F%3E%3Ccircle%20cx%3D'52'%20cy%3D'8'%20r%3D'8'%2F%3E%3C%2Fsvg%3E");
   --term-icon: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2060%2016'%20preserveAspectRatio%3D'xMidYMid%20meet'%3E%3Ccircle%20cx%3D'8'%20cy%3D'8'%20r%3D'8'%2F%3E%3Ccircle%20cx%3D'30'%20cy%3D'8'%20r%3D'8'%2F%3E%3Ccircle%20cx%3D'52'%20cy%3D'8'%20r%3D'8'%2F%3E%3C%2Fsvg%3E");
 }
 }
 
 
-[data-element-button-text] {
-  cursor: pointer;
-  appearance: none;
-  background-color: transparent;
-  border: none;
-  padding: 0;
-  color: var(--sl-color-text-secondary);
-
-  &:hover {
-    color: var(--sl-color-text);
-  }
-}
-
 [data-element-button-text] {
 [data-element-button-text] {
   cursor: pointer;
   cursor: pointer;
   appearance: none;
   appearance: none;
@@ -59,13 +46,18 @@
   flex-direction: column;
   flex-direction: column;
   gap: 0.75rem;
   gap: 0.75rem;
 
 
+  @media (max-width: 30rem) {
+    gap: 1rem;
+  }
+
   [data-section="title"] {
   [data-section="title"] {
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
     justify-content: space-between;
     justify-content: space-between;
-    gap: 1rem;
+    gap: 3rem;
 
 
     & > div {
     & > div {
+      flex: 0 0 auto;
       display: flex;
       display: flex;
       flex-direction: column;
       flex-direction: column;
       gap: 0.5rem;
       gap: 0.5rem;
@@ -127,6 +119,11 @@
     -webkit-box-orient: vertical;
     -webkit-box-orient: vertical;
     -webkit-line-clamp: 2;
     -webkit-line-clamp: 2;
     overflow: hidden;
     overflow: hidden;
+
+    @media (max-width: 30rem) {
+      font-size: 1.25rem;
+      -webkit-line-clamp: 3;
+    }
   }
   }
 
 
   [data-section="stats"] {
   [data-section="stats"] {
@@ -163,9 +160,33 @@
       }
       }
 
 
       span[data-stat-model] {
       span[data-stat-model] {
-        color: var(sl-color-text);
+        color: var(--sl-color-text);
+      }
+    }
+  }
+  [data-section="system-prompt"] {
+    display: flex;
+    gap: 0.3125rem;
+
+    [data-section="icon"] {
+      flex: 0 0 auto;
+      color: var(--sl-color-text-dimmed);
+      opacity: 0.85;
+      svg {
+        display: block;
       }
       }
     }
     }
+
+    [data-section="content"] {
+      display: flex;
+      flex-direction: column;
+      gap: 0.5rem;
+    }
+
+    button {
+      line-height: 1rem;
+      font-size: 0.875rem;
+    }
   }
   }
 }
 }