Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@
<option value="">Load a sample…</option>
</select>
</label>
<label class="file">
<input type="file" id="schema-file" accept=".proto,text/plain" />
<span>Or open a .proto…</span>
</label>
</div>
<div class="editor" id="schema-editor"></div>
</div>
Expand Down Expand Up @@ -146,7 +150,10 @@
<option value="">choose a message…</option>
</select>
</label>
<button type="button" class="ghost" id="copy-schema">Copy from Schema &rarr; code</button>
<label class="file">
<input type="file" id="decode-schema-file" accept=".proto,text/plain" />
<span>Or open a .proto…</span>
</label>
</form>
<div class="editor" id="decode-schema-editor"></div>
<div class="messages" id="decode-schema-messages" aria-live="polite"></div>
Expand Down
29 changes: 22 additions & 7 deletions web/src/decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { parsePayload, formatBytes, toHex, type PayloadFormat } from './payload'
import { renderTree, setAllOpen } from './tree';
import { createEditor, setContent, showErrors, clearErrors } from './editor';
import { decodeSamples } from './decodeSamples';
import { schemaViewText } from './schema';
import type { DecodeRequest, DecodeResult, SchemaError } from './types';

const DEBOUNCE_MS = 200;
Expand Down Expand Up @@ -33,7 +32,7 @@ export function initDecodeView(): void {
const rootSelect = required<HTMLSelectElement>('#opt-root-type');
const schemaMessages = required('#decode-schema-messages');
const schemaState = required('#decode-schema-state');
const copySchemaButton = required<HTMLButtonElement>('#copy-schema');
const schemaFile = required<HTMLInputElement>('#decode-schema-file');

const schemaEditor = createEditor({
parent: required('#decode-schema-editor'),
Expand Down Expand Up @@ -122,11 +121,27 @@ export function initDecodeView(): void {
})();
});

copySchemaButton.addEventListener('click', () => {
setContent(schemaEditor, schemaViewText());
schemaPanel.open = true;
cancelSchema();
void refreshSchema();
// Read here in the browser, like the payload file above it: the schema names the fields for a
// decode that is already happening locally, so opening one sends nothing anywhere.
schemaFile.addEventListener('change', () => {
const file = schemaFile.files?.[0];
// so that picking the same file again reloads it, rather than being a no-op change event
schemaFile.value = '';
if (!file) return;

void (async () => {
let text: string;
try {
text = await file.text();
} catch (error) {
renderMessages(schemaMessages, [], `could not read ${file.name}: ${String(error)}`);
return;
}
setContent(schemaEditor, text);
schemaPanel.open = true;
cancelSchema();
await refreshSchema();
})();
});

// the editor is built inside a closed disclosure, so it has nothing to measure against until
Expand Down
35 changes: 21 additions & 14 deletions web/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,12 @@ const DEFAULT_SCHEMA = samples[0]!.schema ?? '';
/** Small enough that regenerating on every keystroke is fine; this just avoids thrashing. */
const DEBOUNCE_MS = 250;

let latestSchema = DEFAULT_SCHEMA;

/**
* The .proto this view currently holds, so the decode view can offer to borrow it. Kept here
* rather than read out of the DOM, because the editor's document is the authority on its own text.
*/
export function schemaViewText(): string {
return latestSchema;
}

export function initSchemaView(): void {
const schemaEditor = createEditor({
parent: required('#schema-editor'),
doc: DEFAULT_SCHEMA,
language: 'protobuf',
onChange: (value) => {
latestSchema = value;
scheduleGenerate('edit');
},
onChange: () => scheduleGenerate('edit'),
});

const outputEditor = createEditor({
Expand All @@ -58,6 +45,7 @@ export function initSchemaView(): void {
const fileTabs = required('#file-tabs');
const copyButton = required<HTMLButtonElement>('#copy-output');
const samplePicker = required<HTMLSelectElement>('#sample-picker');
const schemaFile = required<HTMLInputElement>('#schema-file');
const outputPane = required('#output-pane');
const staleBadge = required('#stale-badge');
const privacyBadge = required('#privacy-badge');
Expand Down Expand Up @@ -107,6 +95,25 @@ export function initSchemaView(): void {
})();
});

// The file is read here in the browser; opening one sends nothing anywhere, exactly as if its
// text had been pasted in. What happens on generate is unchanged, and the badge still says so.
schemaFile.addEventListener('change', () => {
const file = schemaFile.files?.[0];
// so that picking the same file again reloads it, rather than being a no-op change event
schemaFile.value = '';
if (!file) return;

void (async () => {
try {
setContent(schemaEditor, await file.text());
} catch (error) {
renderMessages(messages, [], `could not read ${file.name}: ${String(error)}`);
return;
}
scheduleGenerate('edit');
})();
});

// Picking a target is itself the decision to use it, so this generates even for a protoc target;
// what does not happen automatically is sending the schema again on every later keystroke.
optionsForm.addEventListener('change', () => {
Expand Down
9 changes: 6 additions & 3 deletions web/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -431,20 +431,23 @@ button.ghost:disabled {
font-size: 13px;
}

.options label.file {
.options label.file,
.pane-head label.file {
position: relative;
overflow: hidden;
cursor: pointer;
}

.options label.file input[type='file'] {
.options label.file input[type='file'],
.pane-head label.file input[type='file'] {
position: absolute;
inset: 0;
opacity: 0;
cursor: pointer;
}

.options label.file span {
.options label.file span,
.pane-head label.file span {
padding: 4px 10px;
border: 1px dashed var(--border-strong);
border-radius: 4px;
Expand Down
Loading