@hozu/devtools 0.17.2 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/messages.d.ts +413 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +425 -0
- package/dist/messages.js.map +1 -0
- package/dist/overlay/app.js +203 -182
- package/dist/overlay/app.js.map +1 -1
- package/dist/overlay/assets.d.ts.map +1 -1
- package/dist/overlay/assets.js +53 -52
- package/dist/overlay/assets.js.map +1 -1
- package/dist/overlay/effects.d.ts.map +1 -1
- package/dist/overlay/effects.js +86 -79
- package/dist/overlay/effects.js.map +1 -1
- package/dist/overlay/index.js +8 -2
- package/dist/overlay/index.js.map +1 -1
- package/dist/overlay/layers.d.ts.map +1 -1
- package/dist/overlay/layers.js +27 -22
- package/dist/overlay/layers.js.map +1 -1
- package/dist/overlay/look.d.ts.map +1 -1
- package/dist/overlay/look.js +35 -22
- package/dist/overlay/look.js.map +1 -1
- package/dist/overlay/notes.d.ts.map +1 -1
- package/dist/overlay/notes.js +25 -20
- package/dist/overlay/notes.js.map +1 -1
- package/dist/overlay/text.d.ts.map +1 -1
- package/dist/overlay/text.js +6 -5
- package/dist/overlay/text.js.map +1 -1
- package/dist/plain.d.ts.map +1 -1
- package/dist/plain.js +47 -44
- package/dist/plain.js.map +1 -1
- package/package.json +2 -2
package/dist/overlay/app.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t } from '../messages.js';
|
|
1
2
|
import { describeFor, friendlyName, questionFor } from '../plain.js';
|
|
2
3
|
import { joinRequests, labelOf, openRequestsLine, requestMarkdown, scopesFor, } from '../prompt.js';
|
|
3
4
|
import { held, hold, node, one, page, remove, save, saved, theme, tree } from './api.js';
|
|
@@ -44,7 +45,7 @@ const dock = h('div', { class: 'dock', role: 'toolbar', 'aria-label': 'Hozu DevT
|
|
|
44
45
|
const panel = h('div', {
|
|
45
46
|
class: 'panel',
|
|
46
47
|
role: 'dialog',
|
|
47
|
-
'aria-label': '
|
|
48
|
+
'aria-label': t('panel.aria'),
|
|
48
49
|
hidden: true,
|
|
49
50
|
});
|
|
50
51
|
const bench = h('div', { class: 'bench', hidden: true });
|
|
@@ -146,7 +147,7 @@ async function screensSection() {
|
|
|
146
147
|
if (!out?.route || !out.previews.length)
|
|
147
148
|
return null;
|
|
148
149
|
return h('div', { class: 'sec' }, [
|
|
149
|
-
h('div', { class: 'label' }, ['
|
|
150
|
+
h('div', { class: 'label' }, [t('screens.title')]),
|
|
150
151
|
...out.previews.map((p) => {
|
|
151
152
|
const value = `${out.route}:${p.name}`;
|
|
152
153
|
const on = out.current === value;
|
|
@@ -159,7 +160,7 @@ async function screensSection() {
|
|
|
159
160
|
setScreen(on ? null : value);
|
|
160
161
|
win.location.reload();
|
|
161
162
|
},
|
|
162
|
-
}, [on ? '
|
|
163
|
+
}, [t(on ? 'screens.exit' : 'screens.preview')]),
|
|
163
164
|
]);
|
|
164
165
|
}),
|
|
165
166
|
]);
|
|
@@ -271,7 +272,9 @@ function tagText(n, el) {
|
|
|
271
272
|
if (state.audience === 'builder')
|
|
272
273
|
return [
|
|
273
274
|
friendlyName(n),
|
|
274
|
-
n.component && n.component.uses > 1
|
|
275
|
+
n.component && n.component.uses > 1
|
|
276
|
+
? h('b', {}, [` ${t('hover.shared', { count: n.component.uses })}`])
|
|
277
|
+
: '',
|
|
275
278
|
];
|
|
276
279
|
return [
|
|
277
280
|
h('b', {}, [n.kind === 'element' ? '' : `${n.kind} `]),
|
|
@@ -580,8 +583,8 @@ function renderDock() {
|
|
|
580
583
|
role: 'button',
|
|
581
584
|
tabindex: '0',
|
|
582
585
|
'aria-expanded': String(!state.folded),
|
|
583
|
-
'aria-label': state.folded ? '
|
|
584
|
-
title: '
|
|
586
|
+
'aria-label': t(state.folded ? 'dock.open' : 'dock.fold'),
|
|
587
|
+
title: t('dock.grip'),
|
|
585
588
|
onkeydown: (e) => {
|
|
586
589
|
const key = e.key;
|
|
587
590
|
if (key === 'Enter' || key === ' ') {
|
|
@@ -598,12 +601,12 @@ function renderDock() {
|
|
|
598
601
|
}
|
|
599
602
|
dock.replaceChildren(...present([
|
|
600
603
|
grip,
|
|
601
|
-
h('div', { class: 'seg' }, [button('
|
|
604
|
+
h('div', { class: 'seg' }, [button(t('dock.browse'), 'browse'), button(t('dock.select'), 'select')]),
|
|
602
605
|
agent.count()
|
|
603
606
|
? h('button', {
|
|
604
607
|
class: 'act agent',
|
|
605
608
|
type: 'button',
|
|
606
|
-
title: '
|
|
609
|
+
title: t('dock.agent.title'),
|
|
607
610
|
'aria-pressed': String(state.panel === 'notes'),
|
|
608
611
|
onclick: () => {
|
|
609
612
|
if (state.panel === 'notes')
|
|
@@ -611,7 +614,7 @@ function renderDock() {
|
|
|
611
614
|
open('notes');
|
|
612
615
|
agent.focus();
|
|
613
616
|
},
|
|
614
|
-
}, ['
|
|
617
|
+
}, [t('dock.agent'), h('span', { class: 'agent-count' }, [String(agent.count())])])
|
|
615
618
|
: null,
|
|
616
619
|
h('button', {
|
|
617
620
|
class: 'act',
|
|
@@ -622,54 +625,54 @@ function renderDock() {
|
|
|
622
625
|
else
|
|
623
626
|
open('changes', null, drafted().length ? 'draft' : 'saved');
|
|
624
627
|
},
|
|
625
|
-
}, ['
|
|
628
|
+
}, [t('dock.changes'), h('span', { class: 'count', 'data-count': true })]),
|
|
626
629
|
h('button', {
|
|
627
630
|
class: 'act',
|
|
628
631
|
type: 'button',
|
|
629
|
-
title: '
|
|
632
|
+
title: t('dock.page.title'),
|
|
630
633
|
onclick: async () => {
|
|
631
634
|
const n = await page(win.location.pathname);
|
|
632
635
|
if (n)
|
|
633
636
|
select({ id: n.id, index: 0, visible: doc.title }, state.mode === 'select' && state.picks.length > 0);
|
|
634
637
|
},
|
|
635
|
-
}, ['
|
|
638
|
+
}, [t('dock.page')]),
|
|
636
639
|
h('button', {
|
|
637
640
|
class: 'act',
|
|
638
641
|
type: 'button',
|
|
639
|
-
title: '
|
|
642
|
+
title: t('dock.layers.title'),
|
|
640
643
|
onclick: () => {
|
|
641
644
|
state.panel = state.panel === 'layers' ? (state.picks.length ? 'inspector' : null) : 'layers';
|
|
642
645
|
persist();
|
|
643
646
|
void renderPanel();
|
|
644
647
|
},
|
|
645
|
-
}, ['
|
|
648
|
+
}, [t('dock.layers')]),
|
|
646
649
|
h('button', {
|
|
647
650
|
class: 'act',
|
|
648
651
|
type: 'button',
|
|
649
|
-
title: '
|
|
652
|
+
title: t('dock.assets.title'),
|
|
650
653
|
onclick: () => void assets.open(),
|
|
651
|
-
}, ['
|
|
654
|
+
}, [t('dock.assets')]),
|
|
652
655
|
h('button', {
|
|
653
656
|
class: 'act',
|
|
654
657
|
type: 'button',
|
|
655
|
-
title: '
|
|
658
|
+
title: t('dock.api.title'),
|
|
656
659
|
'aria-pressed': api.isOpen() ? 'true' : 'false',
|
|
657
660
|
onclick: () => api.toggle(),
|
|
658
|
-
}, ['
|
|
661
|
+
}, [t('dock.api')]),
|
|
659
662
|
h('button', {
|
|
660
663
|
class: 'act',
|
|
661
664
|
type: 'button',
|
|
662
|
-
title: '
|
|
665
|
+
title: t('dock.frame.title'),
|
|
663
666
|
onclick: () => {
|
|
664
667
|
state.panel = state.picks.length ? 'inspector' : null;
|
|
665
668
|
openBench();
|
|
666
669
|
},
|
|
667
|
-
}, ['
|
|
670
|
+
}, [t('dock.frame')]),
|
|
668
671
|
h('button', {
|
|
669
672
|
class: 'act gear',
|
|
670
673
|
type: 'button',
|
|
671
|
-
title: '
|
|
672
|
-
'aria-label': '
|
|
674
|
+
title: t('dock.settings'),
|
|
675
|
+
'aria-label': t('dock.settings'),
|
|
673
676
|
onclick: () => {
|
|
674
677
|
state.panel = state.panel === 'settings' ? (state.picks.length ? 'inspector' : null) : 'settings';
|
|
675
678
|
persist();
|
|
@@ -680,37 +683,40 @@ function renderDock() {
|
|
|
680
683
|
? h('button', {
|
|
681
684
|
class: 'previewing',
|
|
682
685
|
type: 'button',
|
|
683
|
-
title: '
|
|
686
|
+
title: t('dock.screen.exit'),
|
|
684
687
|
onclick: () => {
|
|
685
688
|
setScreen(null);
|
|
686
689
|
win.location.reload();
|
|
687
690
|
},
|
|
688
|
-
}, [
|
|
691
|
+
}, [
|
|
692
|
+
t('dock.screen', { name: screenOf().slice(screenOf().indexOf(':') + 1) }),
|
|
693
|
+
` · ${t('dock.exit')}`,
|
|
694
|
+
])
|
|
689
695
|
: null,
|
|
690
696
|
held()
|
|
691
697
|
? h('button', {
|
|
692
698
|
class: 'previewing',
|
|
693
699
|
type: 'button',
|
|
694
|
-
title: '
|
|
700
|
+
title: t('dock.preview.exit'),
|
|
695
701
|
onclick: () => hold(null, reload),
|
|
696
|
-
}, [h('span', { 'data-preview': true }, ['
|
|
702
|
+
}, [h('span', { 'data-preview': true }, [t('dock.preview')]), ` · ${t('dock.exit')}`])
|
|
697
703
|
: null,
|
|
698
704
|
state.mode === 'select' && !held() && !state.panel
|
|
699
705
|
? h('div', { class: 'tip', role: 'note' }, [
|
|
700
|
-
h('span', {},
|
|
701
|
-
h('span', {},
|
|
702
|
-
h('span', {},
|
|
703
|
-
h('span', {},
|
|
704
|
-
h('span', {},
|
|
706
|
+
h('span', {}, inline(t('tip.click'), 'key', h('kbd', {}, ['Click']))),
|
|
707
|
+
h('span', {}, inline(t('tip.shift'), 'key', h('kbd', {}, ['Shift']))),
|
|
708
|
+
h('span', {}, inline(t('tip.parent'), 'key', h('kbd', {}, ['⇧ Enter']))),
|
|
709
|
+
h('span', {}, inline(t('tip.measure'), 'key', h('kbd', {}, ['Alt']))),
|
|
710
|
+
h('span', {}, inline(t('tip.stop'), 'key', h('kbd', {}, ['Esc']))),
|
|
705
711
|
])
|
|
706
712
|
: null,
|
|
707
713
|
]));
|
|
708
714
|
const holding = held();
|
|
709
715
|
if (holding)
|
|
710
|
-
void tree(win.location.pathname).then((
|
|
716
|
+
void tree(win.location.pathname).then((found) => {
|
|
711
717
|
const label = dock.querySelector('[data-preview]');
|
|
712
718
|
if (label)
|
|
713
|
-
label.textContent =
|
|
719
|
+
label.textContent = t('dock.preview.named', { name: previewLabel(found, holding) });
|
|
714
720
|
});
|
|
715
721
|
const count = dock.querySelector('[data-count]');
|
|
716
722
|
if (count)
|
|
@@ -817,9 +823,9 @@ const where = (what, l) => l
|
|
|
817
823
|
h('span', {}, [`${l.file}:${l.line}:${l.column}`]),
|
|
818
824
|
h('button', {
|
|
819
825
|
type: 'button',
|
|
820
|
-
title: '
|
|
826
|
+
title: t('inspector.copyLocation'),
|
|
821
827
|
onclick: () => void copy(`${l.file}:${l.line}:${l.column}`),
|
|
822
|
-
}, ['
|
|
828
|
+
}, [t('inspector.copy')]),
|
|
823
829
|
])
|
|
824
830
|
: null;
|
|
825
831
|
function excerpt(n) {
|
|
@@ -838,28 +844,35 @@ const section = (label, children) => children.some(Boolean)
|
|
|
838
844
|
? h('div', { class: 'sec' }, [h('div', { class: 'label' }, [label]), ...children])
|
|
839
845
|
: null;
|
|
840
846
|
const kindTitle = {
|
|
841
|
-
element: '
|
|
842
|
-
component: '
|
|
843
|
-
text: '
|
|
844
|
-
query: '
|
|
845
|
-
when: '
|
|
846
|
-
if: '
|
|
847
|
-
list: '
|
|
848
|
-
html: '
|
|
849
|
-
embed: '
|
|
850
|
-
other: '
|
|
847
|
+
element: 'kind.element',
|
|
848
|
+
component: 'kind.component',
|
|
849
|
+
text: 'kind.text',
|
|
850
|
+
query: 'kind.query',
|
|
851
|
+
when: 'kind.when',
|
|
852
|
+
if: 'kind.if',
|
|
853
|
+
list: 'kind.list',
|
|
854
|
+
html: 'kind.html',
|
|
855
|
+
embed: 'kind.embed',
|
|
856
|
+
other: 'kind.other',
|
|
857
|
+
};
|
|
858
|
+
const kindOf = (kind) => (kindTitle[kind] ? t(kindTitle[kind]) : kind);
|
|
859
|
+
const inline = (text, name, node) => {
|
|
860
|
+
const [before, after] = text.split(`{${name}}`);
|
|
861
|
+
return after === undefined ? [text] : [before || null, node, after || null].filter((x) => x !== null);
|
|
851
862
|
};
|
|
852
863
|
function kicker(n) {
|
|
853
864
|
if (state.audience === 'builder')
|
|
854
|
-
return n.page
|
|
865
|
+
return n.page
|
|
866
|
+
? t('inspector.kicker.page', { path: n.page.path })
|
|
867
|
+
: t('inspector.kicker.on', { path: win.location.pathname });
|
|
855
868
|
const kind = n.component
|
|
856
|
-
? '
|
|
869
|
+
? t('kind.component')
|
|
857
870
|
: n.kind === 'text' && n.source?.kind === 'data'
|
|
858
|
-
? '
|
|
859
|
-
: (
|
|
871
|
+
? t('kind.dataText')
|
|
872
|
+
: kindOf(n.kind);
|
|
860
873
|
if (n.page)
|
|
861
|
-
return
|
|
862
|
-
return
|
|
874
|
+
return t('inspector.kicker.route', { path: n.page.path });
|
|
875
|
+
return t('inspector.kicker.view', { kind, view: `${n.owner?.feature}.${n.owner?.view}` });
|
|
863
876
|
}
|
|
864
877
|
function titleFor(n, active) {
|
|
865
878
|
const text = (active.visible || n.text || '').slice(0, 40);
|
|
@@ -872,7 +885,7 @@ function titleFor(n, active) {
|
|
|
872
885
|
? `<${n.tag}>`
|
|
873
886
|
: n.kind === 'text'
|
|
874
887
|
? `“${text}”`
|
|
875
|
-
: (
|
|
888
|
+
: kindOf(n.kind),
|
|
876
889
|
n.component ? h('code', {}, [n.component.ref]) : null,
|
|
877
890
|
];
|
|
878
891
|
}
|
|
@@ -884,7 +897,9 @@ function textRows(n) {
|
|
|
884
897
|
h('b', {}, [source.kind === 'literal' ? 'text ' : `${source.kind} `]),
|
|
885
898
|
source.kind === 'literal' ? `“${source.detail}”` : source.detail,
|
|
886
899
|
source.location ? ` ${source.location.file}:${source.location.line}` : '',
|
|
887
|
-
(source.uses ?? 1) > 1
|
|
900
|
+
(source.uses ?? 1) > 1
|
|
901
|
+
? h('span', { class: 'warn' }, [` · ${t('inspector.sharedBy', { count: source.uses })}`])
|
|
902
|
+
: null,
|
|
888
903
|
]));
|
|
889
904
|
}
|
|
890
905
|
async function copy(text) {
|
|
@@ -938,7 +953,7 @@ const open = (panelName, opened = null, tab = state.tab) => {
|
|
|
938
953
|
persist();
|
|
939
954
|
void renderPanel();
|
|
940
955
|
};
|
|
941
|
-
const closeButton = (onclick) => h('button', { class: 'close', type: 'button', 'aria-label': '
|
|
956
|
+
const closeButton = (onclick) => h('button', { class: 'close', type: 'button', 'aria-label': t('common.close'), onclick }, ['×']);
|
|
942
957
|
function choose(active, id, visible) {
|
|
943
958
|
state.picks[state.active] = { ...active, id, scope: null, visible };
|
|
944
959
|
persist();
|
|
@@ -959,6 +974,19 @@ function removeActive() {
|
|
|
959
974
|
drawPicks();
|
|
960
975
|
void renderPanel();
|
|
961
976
|
}
|
|
977
|
+
function scopeLabel(n, scope) {
|
|
978
|
+
const c = n.component;
|
|
979
|
+
if (scope === 'component' && c)
|
|
980
|
+
return t(c.uses === 1 ? 'scope.component.one' : 'scope.component.other', {
|
|
981
|
+
name: c.ref.split('.').pop() ?? '',
|
|
982
|
+
count: c.uses,
|
|
983
|
+
});
|
|
984
|
+
if (scope === 'items')
|
|
985
|
+
return t('scope.items');
|
|
986
|
+
if (n.conditions.some((x) => x.kind === 'each'))
|
|
987
|
+
return t('scope.thisItem');
|
|
988
|
+
return t(c ? 'scope.instance' : 'scope.thisOne');
|
|
989
|
+
}
|
|
962
990
|
function scopeField(n, active) {
|
|
963
991
|
const scopes = scopesFor(n);
|
|
964
992
|
if (scopes.length < 2)
|
|
@@ -978,7 +1006,7 @@ function scopeField(n, active) {
|
|
|
978
1006
|
persist();
|
|
979
1007
|
},
|
|
980
1008
|
}),
|
|
981
|
-
asked?.options[s.scope] ?? s.
|
|
1009
|
+
asked?.options[s.scope] ?? scopeLabel(n, s.scope),
|
|
982
1010
|
])),
|
|
983
1011
|
]);
|
|
984
1012
|
}
|
|
@@ -998,7 +1026,7 @@ function exportBlock() {
|
|
|
998
1026
|
const result = h('div', { class: 'status', role: 'status' });
|
|
999
1027
|
const draft = h('textarea', {
|
|
1000
1028
|
class: 'draft',
|
|
1001
|
-
'aria-label': '
|
|
1029
|
+
'aria-label': t('export.draft'),
|
|
1002
1030
|
hidden: true,
|
|
1003
1031
|
});
|
|
1004
1032
|
const generate = async () => requestMarkdown(await request(), { excerpt: state.excerpt, theme: await theme() });
|
|
@@ -1010,10 +1038,10 @@ function exportBlock() {
|
|
|
1010
1038
|
if (draft.hidden)
|
|
1011
1039
|
draft.value = await generate();
|
|
1012
1040
|
draft.hidden = !draft.hidden;
|
|
1013
|
-
edit.textContent = draft.hidden ? '
|
|
1041
|
+
edit.textContent = t(draft.hidden ? 'export.edit' : 'export.discard');
|
|
1014
1042
|
},
|
|
1015
|
-
}, ['
|
|
1016
|
-
const many = count > 1
|
|
1043
|
+
}, [t('export.edit')]);
|
|
1044
|
+
const many = count > 1;
|
|
1017
1045
|
return h('div', { class: 'export' }, [
|
|
1018
1046
|
h('div', { class: 'row-end' }, [edit]),
|
|
1019
1047
|
draft,
|
|
@@ -1023,11 +1051,9 @@ function exportBlock() {
|
|
|
1023
1051
|
type: 'button',
|
|
1024
1052
|
onclick: async () => {
|
|
1025
1053
|
await copy(await markdown());
|
|
1026
|
-
status(result, 'ok', [
|
|
1027
|
-
`Copied ${count === 1 ? 'the change' : `all ${count} changes`}. Paste it to your agent.`,
|
|
1028
|
-
]);
|
|
1054
|
+
status(result, 'ok', [t(count === 1 ? 'export.copied.one' : 'export.copied.other', { count })]);
|
|
1029
1055
|
},
|
|
1030
|
-
}, [
|
|
1056
|
+
}, [many ? t('export.copy.many', { count }) : t('export.copy')]),
|
|
1031
1057
|
h('button', {
|
|
1032
1058
|
type: 'button',
|
|
1033
1059
|
onclick: async () => {
|
|
@@ -1035,16 +1061,16 @@ function exportBlock() {
|
|
|
1035
1061
|
const done = await save(await markdown());
|
|
1036
1062
|
const ask = `Do the Hozu request ${done.file}`;
|
|
1037
1063
|
await copy(ask);
|
|
1038
|
-
notice =
|
|
1064
|
+
notice = inline(t('export.saved', { ask }), 'file', h('code', {}, [done.file]));
|
|
1039
1065
|
clearDraft();
|
|
1040
1066
|
renderDock();
|
|
1041
1067
|
open('changes', null, 'saved');
|
|
1042
1068
|
}
|
|
1043
1069
|
catch (e) {
|
|
1044
|
-
status(result, 'err', [
|
|
1070
|
+
status(result, 'err', [t('export.notSaved', { error: e.message })]);
|
|
1045
1071
|
}
|
|
1046
1072
|
},
|
|
1047
|
-
}, [
|
|
1073
|
+
}, [many ? t('export.save.many', { count }) : t('export.save')]),
|
|
1048
1074
|
h('button', {
|
|
1049
1075
|
type: 'button',
|
|
1050
1076
|
onclick: async () => {
|
|
@@ -1053,9 +1079,9 @@ function exportBlock() {
|
|
|
1053
1079
|
download: 'hozu-request.md',
|
|
1054
1080
|
});
|
|
1055
1081
|
a.click();
|
|
1056
|
-
status(result, 'ok', ['
|
|
1082
|
+
status(result, 'ok', [t('export.downloaded', { file: 'hozu-request.md' })]);
|
|
1057
1083
|
},
|
|
1058
|
-
}, ['
|
|
1084
|
+
}, [t('export.download')]),
|
|
1059
1085
|
]),
|
|
1060
1086
|
result,
|
|
1061
1087
|
]);
|
|
@@ -1081,8 +1107,8 @@ function textFor(n, active) {
|
|
|
1081
1107
|
function requestSection(n, active) {
|
|
1082
1108
|
const others = state.picks.filter((p) => p !== active && hasContent(p)).length;
|
|
1083
1109
|
const note = h('textarea', {
|
|
1084
|
-
'aria-label': '
|
|
1085
|
-
placeholder: '
|
|
1110
|
+
'aria-label': t('change.aria'),
|
|
1111
|
+
placeholder: t('change.placeholder'),
|
|
1086
1112
|
oninput: (event) => {
|
|
1087
1113
|
const had = hasContent(active);
|
|
1088
1114
|
active.note = event.target.value;
|
|
@@ -1093,33 +1119,31 @@ function requestSection(n, active) {
|
|
|
1093
1119
|
});
|
|
1094
1120
|
note.value = active.note;
|
|
1095
1121
|
return h('div', { class: 'sec' }, [
|
|
1096
|
-
h('div', { class: 'label' }, ['
|
|
1122
|
+
h('div', { class: 'label' }, [t('change.label')]),
|
|
1097
1123
|
scopeField(n, active),
|
|
1098
1124
|
note,
|
|
1099
1125
|
others
|
|
1100
1126
|
? h('div', { class: 'plain more' }, [
|
|
1101
|
-
|
|
1127
|
+
t(others === 1 ? 'change.others.one' : 'change.others.other', { count: others }),
|
|
1102
1128
|
h('button', { class: 'link', type: 'button', onclick: () => open('changes', null, 'draft') }, [
|
|
1103
|
-
'
|
|
1129
|
+
t('change.review'),
|
|
1104
1130
|
]),
|
|
1105
1131
|
])
|
|
1106
|
-
: h('div', { class: 'hint-text' }, [
|
|
1107
|
-
'Described parts stay in this request when you select the next one.',
|
|
1108
|
-
]),
|
|
1132
|
+
: h('div', { class: 'hint-text' }, [t('change.hint')]),
|
|
1109
1133
|
exportBlock(),
|
|
1110
1134
|
]);
|
|
1111
1135
|
}
|
|
1112
1136
|
function builderSections(n) {
|
|
1113
1137
|
const children = n.children.filter((c) => c.kind !== 'text');
|
|
1114
1138
|
return [
|
|
1115
|
-
section('
|
|
1139
|
+
section(t('inspector.about'), describeFor(n).map((line) => h('div', { class: 'plain' }, [line]))),
|
|
1116
1140
|
h('div', { class: 'sec' }, [
|
|
1117
1141
|
h('div', { class: 'nav' }, [
|
|
1118
|
-
h('button', { type: 'button', onclick: () => walk('up') }, ['
|
|
1142
|
+
h('button', { type: 'button', onclick: () => walk('up') }, [t('inspector.around')]),
|
|
1119
1143
|
children.length && !isText(n)
|
|
1120
|
-
? h('button', { type: 'button', onclick: () => walk('down') }, ['
|
|
1144
|
+
? h('button', { type: 'button', onclick: () => walk('down') }, [t('inspector.inside')])
|
|
1121
1145
|
: null,
|
|
1122
|
-
h('button', { type: 'button', onclick: removeActive }, ['
|
|
1146
|
+
h('button', { type: 'button', onclick: removeActive }, [t('inspector.remove')]),
|
|
1123
1147
|
]),
|
|
1124
1148
|
n.location
|
|
1125
1149
|
? h('div', { class: 'file' }, [
|
|
@@ -1128,7 +1152,7 @@ function builderSections(n) {
|
|
|
1128
1152
|
class: 'link',
|
|
1129
1153
|
type: 'button',
|
|
1130
1154
|
onclick: () => void copy(`${n.location.file}:${n.location.line}`),
|
|
1131
|
-
}, ['
|
|
1155
|
+
}, [t('inspector.copy')]),
|
|
1132
1156
|
])
|
|
1133
1157
|
: null,
|
|
1134
1158
|
]),
|
|
@@ -1137,25 +1161,27 @@ function builderSections(n) {
|
|
|
1137
1161
|
function developerSections(n, active) {
|
|
1138
1162
|
const children = n.children.filter((c) => c.kind !== 'text' || c.text);
|
|
1139
1163
|
return [
|
|
1140
|
-
section('
|
|
1141
|
-
where(n.page ? 'page' : 'view', n.location),
|
|
1142
|
-
n.page ? where('route', n.page.routeLocation) : null,
|
|
1143
|
-
n.component ? where('component', n.component.declaration) : null,
|
|
1144
|
-
n.events.length ? where('machine', n.machine) : null,
|
|
1164
|
+
section(t('inspector.where'), [
|
|
1165
|
+
where(t(n.page ? 'inspector.where.page' : 'inspector.where.view'), n.location),
|
|
1166
|
+
n.page ? where(t('inspector.where.route'), n.page.routeLocation) : null,
|
|
1167
|
+
n.component ? where(t('inspector.where.component'), n.component.declaration) : null,
|
|
1168
|
+
n.events.length ? where(t('inspector.where.machine'), n.machine) : null,
|
|
1145
1169
|
excerpt(n),
|
|
1146
1170
|
]),
|
|
1147
|
-
section('
|
|
1171
|
+
section(t('inspector.component'), n.component
|
|
1148
1172
|
? [
|
|
1149
1173
|
h('div', {}, [
|
|
1150
1174
|
h('span', { class: 'chip red' }, [n.component.ref]),
|
|
1151
1175
|
h('span', { class: 'chip' }, [
|
|
1152
|
-
|
|
1176
|
+
t(n.component.uses === 1 ? 'inspector.usedIn.one' : 'inspector.usedIn.other', {
|
|
1177
|
+
count: n.component.uses,
|
|
1178
|
+
}),
|
|
1153
1179
|
]),
|
|
1154
1180
|
...Object.entries(n.component.variant).map(([k, v]) => h('span', { class: 'chip' }, [`${k}=${v}`])),
|
|
1155
1181
|
]),
|
|
1156
1182
|
]
|
|
1157
1183
|
: []),
|
|
1158
|
-
section('
|
|
1184
|
+
section(t('inspector.head'), n.page
|
|
1159
1185
|
? [
|
|
1160
1186
|
...['title', 'description', 'image'].map((f) => h('div', { class: 'row' }, [h('b', {}, [`${f} `]), n.page.head[f] ?? '—'])),
|
|
1161
1187
|
h('div', { class: 'row' }, [h('b', {}, ['views ']), n.page.views.join(', ')]),
|
|
@@ -1165,18 +1191,18 @@ function developerSections(n, active) {
|
|
|
1165
1191
|
: null,
|
|
1166
1192
|
]
|
|
1167
1193
|
: []),
|
|
1168
|
-
section('
|
|
1169
|
-
section('
|
|
1194
|
+
section(t('inspector.text'), textRows(n)),
|
|
1195
|
+
section(t('inspector.shownWhen'), n.conditions.map((c) => h('div', { class: 'row' }, [
|
|
1170
1196
|
h('b', {}, [`${c.kind} `]),
|
|
1171
1197
|
c.detail,
|
|
1172
1198
|
c.location ? ` ${c.location.file}:${c.location.line}` : '',
|
|
1173
1199
|
]))),
|
|
1174
|
-
section('
|
|
1200
|
+
section(t('inspector.behaviour'), n.events.map((e) => h('div', { class: 'row' }, [
|
|
1175
1201
|
h('span', { class: 'chip red' }, [`on ${e.dom}`]),
|
|
1176
1202
|
h('span', { class: 'chip' }, [e.event]),
|
|
1177
1203
|
...e.transitions.map((t) => h('span', { class: 'chip' }, [`${t.from} → ${t.to}${t.guarded ? ' if …' : ''}`])),
|
|
1178
1204
|
]))),
|
|
1179
|
-
section('
|
|
1205
|
+
section(t('inspector.insideIt'), [
|
|
1180
1206
|
children.length
|
|
1181
1207
|
? h('div', { class: 'kids' }, children.map((c) => h('button', {
|
|
1182
1208
|
type: 'button',
|
|
@@ -1184,9 +1210,11 @@ function developerSections(n, active) {
|
|
|
1184
1210
|
}, [c.kind === 'text' ? `text ${c.text}` : `${c.kind} ${c.id.split('/').slice(-1)[0]}`])))
|
|
1185
1211
|
: null,
|
|
1186
1212
|
h('div', { class: 'nav' }, [
|
|
1187
|
-
h('button', { type: 'button', onclick: () => walk('up') }, ['
|
|
1188
|
-
isText(n)
|
|
1189
|
-
|
|
1213
|
+
h('button', { type: 'button', onclick: () => walk('up') }, [t('inspector.parent')]),
|
|
1214
|
+
isText(n)
|
|
1215
|
+
? null
|
|
1216
|
+
: h('button', { type: 'button', onclick: () => walk('down') }, [t('inspector.child')]),
|
|
1217
|
+
h('button', { type: 'button', onclick: removeActive }, [t('inspector.remove')]),
|
|
1190
1218
|
]),
|
|
1191
1219
|
]),
|
|
1192
1220
|
h('div', { class: 'sec meta' }, [`node ${n.id}`, n.pointer ? ` · ${n.pointer}` : '']),
|
|
@@ -1212,19 +1240,13 @@ async function renderPanel() {
|
|
|
1212
1240
|
if (state.panel !== 'inspector' || !active) {
|
|
1213
1241
|
panel.hidden = state.view !== 'workbench';
|
|
1214
1242
|
if (state.view === 'workbench')
|
|
1215
|
-
panel.replaceChildren(h('div', { class: 'empty' }, [
|
|
1216
|
-
h('b', {}, ['Nothing selected. ']),
|
|
1217
|
-
'Choose Select and click a part of the page, or pick one in Layers.',
|
|
1218
|
-
]));
|
|
1243
|
+
panel.replaceChildren(h('div', { class: 'empty' }, [h('b', {}, [t('inspector.empty.title')]), t('inspector.empty.text')]));
|
|
1219
1244
|
return;
|
|
1220
1245
|
}
|
|
1221
1246
|
const n = await node(active.id);
|
|
1222
1247
|
if (!n) {
|
|
1223
1248
|
panel.hidden = false;
|
|
1224
|
-
panel.replaceChildren(h('div', { class: 'empty' }, [
|
|
1225
|
-
h('b', {}, ['This part is gone.']),
|
|
1226
|
-
' The code changed since you selected it: select it again.',
|
|
1227
|
-
]));
|
|
1249
|
+
panel.replaceChildren(h('div', { class: 'empty' }, [h('b', {}, [t('inspector.gone.title')]), t('inspector.gone.text')]));
|
|
1228
1250
|
return;
|
|
1229
1251
|
}
|
|
1230
1252
|
const nodes = await Promise.all(state.picks.map((p) => node(p.id)));
|
|
@@ -1269,8 +1291,8 @@ function tabs(current, saved) {
|
|
|
1269
1291
|
onclick: () => open('changes', null, name),
|
|
1270
1292
|
}, [label]);
|
|
1271
1293
|
return h('div', { class: 'tabs' }, [
|
|
1272
|
-
tab('draft',
|
|
1273
|
-
tab('saved',
|
|
1294
|
+
tab('draft', t('changes.tab.draft', { count: drafted().length })),
|
|
1295
|
+
tab('saved', t('changes.tab.saved', { count: saved })),
|
|
1274
1296
|
]);
|
|
1275
1297
|
}
|
|
1276
1298
|
async function renderChanges() {
|
|
@@ -1278,8 +1300,8 @@ async function renderChanges() {
|
|
|
1278
1300
|
const current = state.tab;
|
|
1279
1301
|
panel.hidden = false;
|
|
1280
1302
|
const head = h('div', { class: 'head' }, [
|
|
1281
|
-
h('div', { class: 'kicker' }, [current === 'draft' ? '
|
|
1282
|
-
h('h2', { class: 'title' }, ['
|
|
1303
|
+
h('div', { class: 'kicker' }, [current === 'draft' ? t('changes.notSent') : '.hozu/requests']),
|
|
1304
|
+
h('h2', { class: 'title' }, [t('changes.title')]),
|
|
1283
1305
|
closeButton(() => open(state.picks.length ? 'inspector' : null)),
|
|
1284
1306
|
tabs(current, list.length),
|
|
1285
1307
|
]);
|
|
@@ -1292,17 +1314,17 @@ async function renderChanges() {
|
|
|
1292
1314
|
const done = h('button', {
|
|
1293
1315
|
class: 'link done',
|
|
1294
1316
|
type: 'button',
|
|
1295
|
-
title: '
|
|
1317
|
+
title: t('changes.resolve.title'),
|
|
1296
1318
|
onclick: async () => {
|
|
1297
1319
|
if (!armed) {
|
|
1298
1320
|
armed = true;
|
|
1299
|
-
done.textContent = '
|
|
1321
|
+
done.textContent = t('changes.resolve.confirm');
|
|
1300
1322
|
return;
|
|
1301
1323
|
}
|
|
1302
1324
|
await remove(r.number);
|
|
1303
1325
|
void renderPanel();
|
|
1304
1326
|
},
|
|
1305
|
-
}, ['
|
|
1327
|
+
}, [t('changes.resolve')]);
|
|
1306
1328
|
return h('div', { class: 'req' }, [
|
|
1307
1329
|
h('span', { class: 'n' }, [r.number]),
|
|
1308
1330
|
h('button', { class: 't open-req', type: 'button', onclick: () => open('changes', r.number, 'saved') }, [r.title]),
|
|
@@ -1315,7 +1337,7 @@ async function renderChanges() {
|
|
|
1315
1337
|
message ? h('div', { class: 'notice' }, message) : null,
|
|
1316
1338
|
...(list.length
|
|
1317
1339
|
? [...list].reverse().map(row)
|
|
1318
|
-
: [h('div', { class: 'empty' }, ['
|
|
1340
|
+
: [h('div', { class: 'empty' }, [t('changes.noRequests')])]),
|
|
1319
1341
|
list.length
|
|
1320
1342
|
? h('div', { class: 'sec' }, [
|
|
1321
1343
|
h('div', { class: 'choice' }, [
|
|
@@ -1325,24 +1347,18 @@ async function renderChanges() {
|
|
|
1325
1347
|
onclick: async () => {
|
|
1326
1348
|
const full = await Promise.all(list.map((r) => one(r.number)));
|
|
1327
1349
|
await copy(joinRequests(full));
|
|
1328
|
-
status(result, 'ok', [
|
|
1329
|
-
`Copied all ${list.length} as one prompt. Paste it to your agent.`,
|
|
1330
|
-
]);
|
|
1350
|
+
status(result, 'ok', [t('changes.copiedAll', { count: list.length })]);
|
|
1331
1351
|
},
|
|
1332
|
-
}, [
|
|
1333
|
-
h('span', { class: 'why' }, [
|
|
1334
|
-
'One prompt with every open request: the fewest tokens and no file reads. Works in any chat.',
|
|
1335
|
-
]),
|
|
1352
|
+
}, [t('changes.copyAll', { count: list.length })]),
|
|
1353
|
+
h('span', { class: 'why' }, [t('changes.copyAll.why')]),
|
|
1336
1354
|
h('button', {
|
|
1337
1355
|
type: 'button',
|
|
1338
1356
|
onclick: async () => {
|
|
1339
1357
|
await copy(openRequestsLine(list));
|
|
1340
|
-
status(result, 'ok', ['
|
|
1358
|
+
status(result, 'ok', [t('changes.copiedPaths')]);
|
|
1341
1359
|
},
|
|
1342
|
-
}, ['
|
|
1343
|
-
h('span', { class: 'why' }, [
|
|
1344
|
-
'For an agent in this project that should read the files itself (or run hozu requests --full).',
|
|
1345
|
-
]),
|
|
1360
|
+
}, [t('changes.copyPaths')]),
|
|
1361
|
+
h('span', { class: 'why' }, [t('changes.copyPaths.why')]),
|
|
1346
1362
|
]),
|
|
1347
1363
|
result,
|
|
1348
1364
|
])
|
|
@@ -1365,7 +1381,9 @@ async function renderChanges() {
|
|
|
1365
1381
|
[
|
|
1366
1382
|
p.note.trim(),
|
|
1367
1383
|
p.style?.length
|
|
1368
|
-
?
|
|
1384
|
+
? t(p.style.length === 1 ? 'changes.styles.one' : 'changes.styles.other', {
|
|
1385
|
+
count: p.style.length,
|
|
1386
|
+
})
|
|
1369
1387
|
: '',
|
|
1370
1388
|
]
|
|
1371
1389
|
.filter(Boolean)
|
|
@@ -1379,7 +1397,7 @@ async function renderChanges() {
|
|
|
1379
1397
|
state.active = state.picks.indexOf(p);
|
|
1380
1398
|
open('inspector');
|
|
1381
1399
|
},
|
|
1382
|
-
}, ['
|
|
1400
|
+
}, [t('changes.edit')]),
|
|
1383
1401
|
h('button', {
|
|
1384
1402
|
class: 'link',
|
|
1385
1403
|
type: 'button',
|
|
@@ -1393,13 +1411,10 @@ async function renderChanges() {
|
|
|
1393
1411
|
renderDock();
|
|
1394
1412
|
void renderPanel();
|
|
1395
1413
|
},
|
|
1396
|
-
}, ['
|
|
1414
|
+
}, [t('changes.remove')]),
|
|
1397
1415
|
]);
|
|
1398
1416
|
}))
|
|
1399
|
-
: h('div', { class: 'empty' }, [
|
|
1400
|
-
h('b', {}, ['Nothing yet. ']),
|
|
1401
|
-
'Choose Select, click a part and describe the change. Every described part is added here.',
|
|
1402
|
-
]),
|
|
1417
|
+
: h('div', { class: 'empty' }, [h('b', {}, [t('changes.empty.title')]), t('changes.empty.text')]),
|
|
1403
1418
|
items.length ? h('div', { class: 'sec' }, [exportBlock()]) : null,
|
|
1404
1419
|
]));
|
|
1405
1420
|
}
|
|
@@ -1420,17 +1435,17 @@ async function renderRequest(number) {
|
|
|
1420
1435
|
onclick: async () => {
|
|
1421
1436
|
if (!armed) {
|
|
1422
1437
|
armed = true;
|
|
1423
|
-
del.textContent = '
|
|
1438
|
+
del.textContent = t('request.remove.confirm');
|
|
1424
1439
|
return;
|
|
1425
1440
|
}
|
|
1426
1441
|
await remove(r.number);
|
|
1427
1442
|
renderDock();
|
|
1428
1443
|
open('changes', null, 'saved');
|
|
1429
1444
|
},
|
|
1430
|
-
}, ['
|
|
1445
|
+
}, [t('request.remove')]);
|
|
1431
1446
|
panel.replaceChildren(h('div', { class: 'head' }, [
|
|
1432
1447
|
h('button', { class: 'back', type: 'button', onclick: () => open('changes', null, 'saved') }, [
|
|
1433
|
-
'
|
|
1448
|
+
t('request.back'),
|
|
1434
1449
|
]),
|
|
1435
1450
|
h('h2', { class: 'title' }, [r.title]),
|
|
1436
1451
|
h('div', { class: 'kicker' }, [r.file]),
|
|
@@ -1442,16 +1457,16 @@ async function renderRequest(number) {
|
|
|
1442
1457
|
type: 'button',
|
|
1443
1458
|
onclick: async () => {
|
|
1444
1459
|
await copy(`Do the Hozu request ${r.file}`);
|
|
1445
|
-
status(result, 'ok', [`
|
|
1460
|
+
status(result, 'ok', [t('request.copiedAsk', { ask: `Do the Hozu request ${r.file}` })]);
|
|
1446
1461
|
},
|
|
1447
|
-
}, ['
|
|
1462
|
+
}, [t('request.copyAsk')]),
|
|
1448
1463
|
h('button', {
|
|
1449
1464
|
type: 'button',
|
|
1450
1465
|
onclick: async () => {
|
|
1451
1466
|
await copy(r.markdown);
|
|
1452
|
-
status(result, 'ok', ['
|
|
1467
|
+
status(result, 'ok', [t('request.copied')]);
|
|
1453
1468
|
},
|
|
1454
|
-
}, ['
|
|
1469
|
+
}, [t('request.copy')]),
|
|
1455
1470
|
del,
|
|
1456
1471
|
]),
|
|
1457
1472
|
result,
|
|
@@ -1475,14 +1490,14 @@ function renderSettings() {
|
|
|
1475
1490
|
]);
|
|
1476
1491
|
panel.replaceChildren(h('div', { class: 'head' }, [
|
|
1477
1492
|
h('div', { class: 'kicker' }, ['Hozu DevTools']),
|
|
1478
|
-
h('h2', { class: 'title' }, ['
|
|
1493
|
+
h('h2', { class: 'title' }, [t('settings.title')]),
|
|
1479
1494
|
closeButton(() => open(state.picks.length ? 'inspector' : null)),
|
|
1480
1495
|
]), h('div', { class: 'sec' }, [
|
|
1481
|
-
h('div', { class: 'label' }, ['
|
|
1482
|
-
h('div', { class: 'tabs', role: 'radiogroup', 'aria-label': '
|
|
1483
|
-
['system', '
|
|
1484
|
-
['light', '
|
|
1485
|
-
['dark', '
|
|
1496
|
+
h('div', { class: 'label' }, [t('settings.appearance')]),
|
|
1497
|
+
h('div', { class: 'tabs', role: 'radiogroup', 'aria-label': t('settings.appearance') }, [
|
|
1498
|
+
['system', t('settings.system')],
|
|
1499
|
+
['light', t('settings.light')],
|
|
1500
|
+
['dark', t('settings.dark')],
|
|
1486
1501
|
].map(([value, label]) => h('button', {
|
|
1487
1502
|
class: 'tab',
|
|
1488
1503
|
type: 'button',
|
|
@@ -1497,13 +1512,13 @@ function renderSettings() {
|
|
|
1497
1512
|
},
|
|
1498
1513
|
}, [label]))),
|
|
1499
1514
|
]), h('div', { class: 'sec' }, [
|
|
1500
|
-
h('div', { class: 'label' }, ['
|
|
1515
|
+
h('div', { class: 'label' }, [t('settings.show')]),
|
|
1501
1516
|
h('fieldset', {}, [
|
|
1502
|
-
choice('builder', '
|
|
1503
|
-
choice('developer', '
|
|
1517
|
+
choice('builder', t('settings.builder'), t('settings.builder.detail')),
|
|
1518
|
+
choice('developer', t('settings.developer'), t('settings.developer.detail')),
|
|
1504
1519
|
]),
|
|
1505
1520
|
]), h('div', { class: 'sec' }, [
|
|
1506
|
-
h('div', { class: 'label' }, ['
|
|
1521
|
+
h('div', { class: 'label' }, [t('settings.requests')]),
|
|
1507
1522
|
h('label', { class: 'option' }, [
|
|
1508
1523
|
h('input', {
|
|
1509
1524
|
type: 'checkbox',
|
|
@@ -1513,26 +1528,21 @@ function renderSettings() {
|
|
|
1513
1528
|
persist();
|
|
1514
1529
|
},
|
|
1515
1530
|
}),
|
|
1516
|
-
h('span', {}, [
|
|
1517
|
-
h('b', {}, ['Add the code excerpt']),
|
|
1518
|
-
h('span', {}, [
|
|
1519
|
-
'Seven lines around each place. The agent reads the file anyway, so it is off by default.',
|
|
1520
|
-
]),
|
|
1521
|
-
]),
|
|
1531
|
+
h('span', {}, [h('b', {}, [t('settings.excerpt')]), h('span', {}, [t('settings.excerpt.detail')])]),
|
|
1522
1532
|
]),
|
|
1523
1533
|
]), h('div', { class: 'sec' }, [
|
|
1524
|
-
h('div', { class: 'label' }, ['
|
|
1534
|
+
h('div', { class: 'label' }, [t('settings.keys')]),
|
|
1525
1535
|
...[
|
|
1526
|
-
['Alt+Shift+S', '
|
|
1527
|
-
['Click', '
|
|
1528
|
-
['Shift+click', '
|
|
1529
|
-
['Shift+Enter', '
|
|
1530
|
-
['Enter', '
|
|
1531
|
-
['Tab / Shift+Tab', '
|
|
1532
|
-
['Alt (hold)', '
|
|
1533
|
-
['Double-click', '
|
|
1534
|
-
['↑ ↓', '
|
|
1535
|
-
['Esc', '
|
|
1536
|
+
['Alt+Shift+S', t('keys.toggle')],
|
|
1537
|
+
['Click', t('keys.click')],
|
|
1538
|
+
['Shift+click', t('keys.add')],
|
|
1539
|
+
['Shift+Enter', t('keys.around')],
|
|
1540
|
+
['Enter', t('keys.inside')],
|
|
1541
|
+
['Tab / Shift+Tab', t('keys.beside')],
|
|
1542
|
+
['Alt (hold)', t('keys.measure')],
|
|
1543
|
+
['Double-click', t('keys.text')],
|
|
1544
|
+
['↑ ↓', t('keys.arrows')],
|
|
1545
|
+
['Esc', t('keys.escape')],
|
|
1536
1546
|
].map(([k, v]) => h('div', { class: 'keyrow' }, [h('kbd', {}, [k]), v])),
|
|
1537
1547
|
]));
|
|
1538
1548
|
}
|
|
@@ -1542,6 +1552,12 @@ const devices = [
|
|
|
1542
1552
|
{ name: 'Laptop', width: 1280, height: 800 },
|
|
1543
1553
|
{ name: 'Desktop', width: 1440, height: 900 },
|
|
1544
1554
|
];
|
|
1555
|
+
const deviceNames = {
|
|
1556
|
+
Phone: 'bench.phone',
|
|
1557
|
+
Tablet: 'bench.tablet',
|
|
1558
|
+
Laptop: 'bench.laptop',
|
|
1559
|
+
Desktop: 'bench.desktop',
|
|
1560
|
+
};
|
|
1545
1561
|
const left = h('div', { class: 'bench-left' });
|
|
1546
1562
|
const stage = h('div', { class: 'stage' });
|
|
1547
1563
|
const sizer = h('div', { class: 'sizer' });
|
|
@@ -1595,21 +1611,23 @@ function renderBar() {
|
|
|
1595
1611
|
onclick: () => setMode(value),
|
|
1596
1612
|
}, [label]);
|
|
1597
1613
|
const select = h('select', {
|
|
1598
|
-
'aria-label': '
|
|
1614
|
+
'aria-label': t('bench.device'),
|
|
1599
1615
|
onchange: (e) => {
|
|
1600
1616
|
const d = devices.find((x) => x.name === e.target.value);
|
|
1601
1617
|
if (d)
|
|
1602
1618
|
setDevice(d);
|
|
1603
1619
|
},
|
|
1604
1620
|
}, [
|
|
1605
|
-
...devices.map((d) => h('option', { value: d.name, selected: d.name === state.device.name }, [
|
|
1621
|
+
...devices.map((d) => h('option', { value: d.name, selected: d.name === state.device.name }, [
|
|
1622
|
+
t('bench.option', { name: t(deviceNames[d.name]), width: d.width }),
|
|
1623
|
+
])),
|
|
1606
1624
|
devices.some((d) => d.name === state.device.name)
|
|
1607
1625
|
? null
|
|
1608
|
-
: h('option', { value: 'Custom', selected: true }, ['
|
|
1626
|
+
: h('option', { value: 'Custom', selected: true }, [t('bench.custom')]),
|
|
1609
1627
|
]);
|
|
1610
1628
|
bar.replaceChildren(...present([
|
|
1611
1629
|
h('div', { class: 'grip' }, [h('img', { src: logo, alt: 'Hozu', width: '18', height: '18' })]),
|
|
1612
|
-
h('div', { class: 'seg' }, [mode('
|
|
1630
|
+
h('div', { class: 'seg' }, [mode(t('dock.browse'), 'browse'), mode(t('dock.select'), 'select')]),
|
|
1613
1631
|
h('button', {
|
|
1614
1632
|
class: 'act layers-toggle',
|
|
1615
1633
|
type: 'button',
|
|
@@ -1618,47 +1636,50 @@ function renderBar() {
|
|
|
1618
1636
|
bench.classList.toggle('layers-open');
|
|
1619
1637
|
renderBar();
|
|
1620
1638
|
},
|
|
1621
|
-
}, ['
|
|
1639
|
+
}, [t('dock.layers')]),
|
|
1622
1640
|
select,
|
|
1623
1641
|
h('button', {
|
|
1624
1642
|
class: 'act',
|
|
1625
1643
|
type: 'button',
|
|
1626
|
-
title: '
|
|
1627
|
-
'aria-label': '
|
|
1644
|
+
title: t('bench.rotate'),
|
|
1645
|
+
'aria-label': t('bench.rotate'),
|
|
1628
1646
|
onclick: () => setDevice({ name: state.device.name, width: state.device.height, height: state.device.width }),
|
|
1629
1647
|
}, ['⟲']),
|
|
1630
1648
|
h('span', { class: 'size', 'data-size': true }),
|
|
1631
1649
|
h('span', { class: 'spacer' }),
|
|
1632
1650
|
held()
|
|
1633
1651
|
? h('button', { class: 'previewing', type: 'button', onclick: () => hold(null, reload) }, [
|
|
1634
|
-
'
|
|
1652
|
+
`${t('dock.preview')} · ${t('dock.exit')}`,
|
|
1635
1653
|
])
|
|
1636
1654
|
: null,
|
|
1637
1655
|
h('button', {
|
|
1638
1656
|
class: 'act',
|
|
1639
1657
|
type: 'button',
|
|
1640
1658
|
onclick: () => open(state.panel === 'changes' ? null : 'changes', null, drafted().length ? 'draft' : 'saved'),
|
|
1641
|
-
}, [
|
|
1659
|
+
}, [
|
|
1660
|
+
t('dock.changes'),
|
|
1661
|
+
h('span', { class: 'count' }, [drafted().length ? String(drafted().length) : '']),
|
|
1662
|
+
]),
|
|
1642
1663
|
h('button', {
|
|
1643
1664
|
class: 'act gear',
|
|
1644
1665
|
type: 'button',
|
|
1645
|
-
'aria-label': '
|
|
1666
|
+
'aria-label': t('dock.settings'),
|
|
1646
1667
|
onclick: () => open(state.panel === 'settings' ? null : 'settings'),
|
|
1647
1668
|
}, ['⚙']),
|
|
1648
1669
|
h('button', {
|
|
1649
1670
|
class: 'act',
|
|
1650
1671
|
type: 'button',
|
|
1651
|
-
title: '
|
|
1672
|
+
title: t('bench.api.title'),
|
|
1652
1673
|
'aria-pressed': api.isOpen() ? 'true' : 'false',
|
|
1653
1674
|
onclick: () => api.toggle(),
|
|
1654
|
-
}, ['
|
|
1675
|
+
}, [t('dock.api')]),
|
|
1655
1676
|
h('button', {
|
|
1656
1677
|
class: 'act',
|
|
1657
1678
|
type: 'button',
|
|
1658
|
-
title: '
|
|
1679
|
+
title: t('dock.assets.title'),
|
|
1659
1680
|
onclick: () => void assets.open(),
|
|
1660
|
-
}, ['
|
|
1661
|
-
h('button', { class: 'act exit', type: 'button', onclick: () => closeBench() }, ['
|
|
1681
|
+
}, [t('dock.assets')]),
|
|
1682
|
+
h('button', { class: 'act exit', type: 'button', onclick: () => closeBench() }, [t('bench.exit')]),
|
|
1662
1683
|
]));
|
|
1663
1684
|
}
|
|
1664
1685
|
async function renderLeft() {
|
|
@@ -1675,7 +1696,7 @@ function openBench() {
|
|
|
1675
1696
|
bench.hidden = false;
|
|
1676
1697
|
const frame = h('iframe', {
|
|
1677
1698
|
src: location.href,
|
|
1678
|
-
title: '
|
|
1699
|
+
title: t('bench.frame'),
|
|
1679
1700
|
'data-hozu-bench': true,
|
|
1680
1701
|
});
|
|
1681
1702
|
frame.addEventListener('load', () => {
|
|
@@ -1686,7 +1707,7 @@ function openBench() {
|
|
|
1686
1707
|
void renderLeft();
|
|
1687
1708
|
void renderPanel();
|
|
1688
1709
|
});
|
|
1689
|
-
const grip = h('div', { class: 'resize', title: '
|
|
1710
|
+
const grip = h('div', { class: 'resize', title: t('common.resize'), 'aria-hidden': 'true' });
|
|
1690
1711
|
grip.addEventListener('pointerdown', (event) => {
|
|
1691
1712
|
event.preventDefault();
|
|
1692
1713
|
grip.setPointerCapture(event.pointerId);
|