File size: 9,873 Bytes
2671e04
 
 
 
 
 
 
 
 
 
adb34c2
 
 
 
 
 
 
 
 
 
 
 
 
2671e04
adb34c2
 
2671e04
adb34c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2671e04
 
adb34c2
 
 
 
 
 
 
 
2671e04
adb34c2
 
 
 
 
 
 
2671e04
 
adb34c2
740e55f
adb34c2
2671e04
adb34c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2671e04
adb34c2
 
740e55f
 
 
 
 
 
 
 
 
 
 
 
 
adb34c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
740e55f
adb34c2
740e55f
adb34c2
 
 
2671e04
 
 
adb34c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2671e04
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>oapix chat client</title>
    <link rel="stylesheet" href="/chatclient/styles.css">
    <script type="module" src="/chatclient/app.js"></script>
  </head>
  <body>
    <main class="client-shell">
      <section class="workspace-card">
        <div class="top-bar">
          <div class="tab-strip" role="tablist" aria-label="Chat client tabs">
            <button class="tab-button is-active" type="button" role="tab" aria-selected="true" data-tab="compose">
              Compose
            </button>
            <button class="tab-button" type="button" role="tab" aria-selected="false" data-tab="output">
              Output
            </button>
            <button class="tab-button" type="button" role="tab" aria-selected="false" data-tab="raw">
              Raw Output
            </button>
          </div>
        </div>
        <p id="status-line" class="sr-only">Ready.</p>

        <section class="tab-panel is-active" role="tabpanel" data-panel="compose">
          <article class="editor-card">
            <div class="editor-toolbar" aria-label="Text formatting tools">
              <button class="format-button" type="button" data-command="bold" aria-label="Bold text">
                <strong>B</strong>
              </button>
              <button class="format-button" type="button" data-command="italic" aria-label="Italic text">
                <em>I</em>
              </button>
              <button class="format-button" type="button" data-command="insertUnorderedList" aria-label="Bullet list">
                List
              </button>
              <button class="format-button" type="button" data-command="removeFormat" aria-label="Clear formatting">
                Clear
              </button>
            </div>

            <div
              id="editor"
              class="rich-editor"
              contenteditable="true"
              spellcheck="true"
              data-placeholder="Write the user message here. Use Ctrl+Enter to send."
            ></div>
          </article>

          <div class="tool-row" aria-label="Request tools">
            <button id="settings-toggle" class="tool-button icon-button" type="button" aria-label="Open settings" title="Settings">
              <svg viewBox="0 0 24 24" aria-hidden="true">
                <path d="M12 8.5A3.5 3.5 0 1 0 12 15.5A3.5 3.5 0 1 0 12 8.5z"></path>
                <path d="M19.4 13.5a7.5 7.5 0 0 0 .1-1.5a7.5 7.5 0 0 0-.1-1.5l2-1.6l-2-3.4l-2.4 1a7.9 7.9 0 0 0-2.6-1.5L14 2h-4l-.4 3a7.9 7.9 0 0 0-2.6 1.5l-2.4-1l-2 3.4l2 1.6A7.5 7.5 0 0 0 4.5 12a7.5 7.5 0 0 0 .1 1.5l-2 1.6l2 3.4l2.4-1a7.9 7.9 0 0 0 2.6 1.5l.4 3h4l.4-3a7.9 7.9 0 0 0 2.6-1.5l2.4 1l2-3.4z"></path>
              </svg>
            </button>

            <button id="attachment-trigger" class="tool-button icon-button" type="button" aria-label="Add attachment" title="Add attachment">
              <svg viewBox="0 0 24 24" aria-hidden="true">
                <path d="M12 5v14"></path>
                <path d="M5 12h14"></path>
              </svg>
            </button>

            <button id="send-button" class="send-button icon-button" type="button" aria-label="Send message" title="Send">
              <svg viewBox="0 0 24 24" aria-hidden="true">
                <path d="M4 12l15-7l-3 7l3 7z"></path>
                <path d="M10 12h9"></path>
              </svg>
            </button>

            <input id="attachment-input" type="file" accept="image/*,audio/*" multiple hidden>
          </div>

          <section id="attachment-picker" class="attachment-picker" hidden>
            <div class="section-head">
              <div>
                <p class="eyebrow">Add Attachment</p>
                <h3>Choose Source</h3>
              </div>
              <p class="section-copy">Use uploaded files or direct links for image and audio inputs.</p>
            </div>

            <div class="picker-mode-row" role="tablist" aria-label="Attachment source options">
              <button id="attachment-mode-upload" class="picker-mode-button is-active" type="button" data-mode="upload">
                Upload Files
              </button>
              <button id="attachment-mode-link" class="picker-mode-button" type="button" data-mode="link">
                File Link
              </button>
            </div>

            <div id="attachment-picker-upload" class="picker-panel is-active">
              <p class="picker-copy">Click <strong>Upload Files</strong> above to open the system picker immediately.</p>
            </div>

            <div id="attachment-picker-link" class="picker-panel" hidden>
              <div class="picker-grid">
                <label>
                  <span>Type</span>
                  <select id="attachment-link-type">
                    <option value="image">Image</option>
                    <option value="audio">Audio</option>
                  </select>
                </label>

                <label class="picker-link-field">
                  <span>File Link</span>
                  <input id="attachment-link-url" type="url" placeholder="https://example.com/file">
                </label>
              </div>

              <button id="attachment-link-add" class="picker-action-button" type="button">Add Link</button>
            </div>
          </section>

          <section class="attachments-card" aria-labelledby="attachments-heading">
            <div class="section-head">
              <div>
                <p class="eyebrow">Attachments</p>
                <h3 id="attachments-heading">Image and Audio Attachments</h3>
              </div>
              <p id="attachment-summary" class="section-copy">No files added.</p>
            </div>
            <div id="attachment-list" class="attachment-list">
              <p class="empty-state">Add an image or audio file or link to include it with the next request.</p>
            </div>
          </section>

          <section id="settings-panel" class="settings-panel" hidden>
            <div class="section-head">
              <div>
                <p class="eyebrow">Settings</p>
                <h3>Request Options</h3>
              </div>
              <p class="section-copy">Endpoint and prompt settings stay local in this browser.</p>
            </div>

            <div class="settings-grid">
              <label>
                <span>Endpoint</span>
                <input id="endpoint" name="endpoint" type="url">
              </label>

              <label>
                <span>Model</span>
                <input id="model" name="model" type="text" value="gpt-4.1-mini" required>
              </label>

              <label class="settings-wide">
                <span>System Prompt</span>
                <textarea id="system-prompt" name="systemPrompt" rows="4" placeholder="Optional system prompt"></textarea>
              </label>

              <label class="toggle-card">
                <span>Audio Output</span>
                <input id="audio-output" name="audioOutput" type="checkbox" checked>
              </label>

              <label>
                <span>Voice</span>
                <select id="voice" name="voice">
                  <option value="alloy">alloy</option>
                  <option value="ash">ash</option>
                  <option value="ballad">ballad</option>
                  <option value="coral">coral</option>
                  <option value="sage">sage</option>
                  <option value="verse">verse</option>
                </select>
              </label>
            </div>
          </section>
        </section>

        <section class="tab-panel" role="tabpanel" data-panel="output" hidden>
          <article class="output-card">
            <div class="section-head">
              <div>
                <p class="eyebrow">Assistant Output</p>
                <h3>Rich Text Page</h3>
              </div>
              <p class="section-copy">Rendered response, returned images, and returned audio live here.</p>
            </div>
            <div id="response-output" class="response-output">
              <p class="empty-state">Send a request to open the assistant response view.</p>
            </div>
          </article>
        </section>

        <section class="tab-panel" role="tabpanel" data-panel="raw" hidden>
          <article class="output-card">
            <div class="section-head">
              <div>
                <p class="eyebrow">Debug</p>
                <h3>Raw Response</h3>
              </div>
              <button id="raw-copy-button" class="secondary-button" type="button">Copy Raw</button>
            </div>
            <p class="section-copy">This tab shows the exact JSON returned by the proxy.</p>
            <pre id="raw-json" class="raw-output">{}</pre>
          </article>
        </section>
      </section>
    </main>

    <section id="attachment-preview" class="attachment-preview" hidden>
      <div class="attachment-preview__backdrop" data-close-preview></div>
      <article class="attachment-preview__card" role="dialog" aria-modal="true" aria-labelledby="attachment-preview-title">
        <div class="attachment-preview__head">
          <div>
            <p class="eyebrow">Attachment Preview</p>
            <h3 id="attachment-preview-title">Preview</h3>
          </div>
          <button id="attachment-preview-close" class="attachment-preview__close" type="button" aria-label="Close preview">
            x
          </button>
        </div>
        <div id="attachment-preview-body" class="attachment-preview__body"></div>
      </article>
    </section>

    <button id="error-toast" class="error-toast" type="button" hidden></button>
  </body>
</html>