@hozu/devtools 0.17.2 → 0.18.1
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 +415 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +427 -0
- package/dist/messages.js.map +1 -0
- package/dist/overlay/app.js +212 -183
- 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,36 @@ 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'), [
|
|
1140
|
+
...describeFor(n).map((line) => h('div', { class: 'plain' }, [line])),
|
|
1141
|
+
n.component?.client
|
|
1142
|
+
? h('div', { class: 'plain' }, [t('plain.about.client', { file: n.component.client.file })])
|
|
1143
|
+
: null,
|
|
1144
|
+
]),
|
|
1116
1145
|
h('div', { class: 'sec' }, [
|
|
1117
1146
|
h('div', { class: 'nav' }, [
|
|
1118
|
-
h('button', { type: 'button', onclick: () => walk('up') }, ['
|
|
1119
|
-
children.length && !isText(n)
|
|
1120
|
-
? h('button', { type: 'button', onclick: () => walk('down') }, ['
|
|
1147
|
+
h('button', { type: 'button', onclick: () => walk('up') }, [t('inspector.around')]),
|
|
1148
|
+
children.length && !isText(n) && !n.component?.client
|
|
1149
|
+
? h('button', { type: 'button', onclick: () => walk('down') }, [t('inspector.inside')])
|
|
1121
1150
|
: null,
|
|
1122
|
-
h('button', { type: 'button', onclick: removeActive }, ['
|
|
1151
|
+
h('button', { type: 'button', onclick: removeActive }, [t('inspector.remove')]),
|
|
1123
1152
|
]),
|
|
1124
1153
|
n.location
|
|
1125
1154
|
? h('div', { class: 'file' }, [
|
|
@@ -1128,7 +1157,7 @@ function builderSections(n) {
|
|
|
1128
1157
|
class: 'link',
|
|
1129
1158
|
type: 'button',
|
|
1130
1159
|
onclick: () => void copy(`${n.location.file}:${n.location.line}`),
|
|
1131
|
-
}, ['
|
|
1160
|
+
}, [t('inspector.copy')]),
|
|
1132
1161
|
])
|
|
1133
1162
|
: null,
|
|
1134
1163
|
]),
|
|
@@ -1137,25 +1166,30 @@ function builderSections(n) {
|
|
|
1137
1166
|
function developerSections(n, active) {
|
|
1138
1167
|
const children = n.children.filter((c) => c.kind !== 'text' || c.text);
|
|
1139
1168
|
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,
|
|
1169
|
+
section(t('inspector.where'), [
|
|
1170
|
+
where(t(n.page ? 'inspector.where.page' : 'inspector.where.view'), n.location),
|
|
1171
|
+
n.page ? where(t('inspector.where.route'), n.page.routeLocation) : null,
|
|
1172
|
+
n.component ? where(t('inspector.where.component'), n.component.declaration) : null,
|
|
1173
|
+
n.events.length ? where(t('inspector.where.machine'), n.machine) : null,
|
|
1145
1174
|
excerpt(n),
|
|
1146
1175
|
]),
|
|
1147
|
-
section('
|
|
1176
|
+
section(t('inspector.component'), n.component
|
|
1148
1177
|
? [
|
|
1149
1178
|
h('div', {}, [
|
|
1150
1179
|
h('span', { class: 'chip red' }, [n.component.ref]),
|
|
1151
1180
|
h('span', { class: 'chip' }, [
|
|
1152
|
-
|
|
1181
|
+
t(n.component.uses === 1 ? 'inspector.usedIn.one' : 'inspector.usedIn.other', {
|
|
1182
|
+
count: n.component.uses,
|
|
1183
|
+
}),
|
|
1153
1184
|
]),
|
|
1154
1185
|
...Object.entries(n.component.variant).map(([k, v]) => h('span', { class: 'chip' }, [`${k}=${v}`])),
|
|
1155
1186
|
]),
|
|
1187
|
+
n.component.client
|
|
1188
|
+
? h('div', { class: 'plain' }, [t('inspector.client', { file: n.component.client.file })])
|
|
1189
|
+
: null,
|
|
1156
1190
|
]
|
|
1157
1191
|
: []),
|
|
1158
|
-
section('
|
|
1192
|
+
section(t('inspector.head'), n.page
|
|
1159
1193
|
? [
|
|
1160
1194
|
...['title', 'description', 'image'].map((f) => h('div', { class: 'row' }, [h('b', {}, [`${f} `]), n.page.head[f] ?? '—'])),
|
|
1161
1195
|
h('div', { class: 'row' }, [h('b', {}, ['views ']), n.page.views.join(', ')]),
|
|
@@ -1165,18 +1199,18 @@ function developerSections(n, active) {
|
|
|
1165
1199
|
: null,
|
|
1166
1200
|
]
|
|
1167
1201
|
: []),
|
|
1168
|
-
section('
|
|
1169
|
-
section('
|
|
1202
|
+
section(t('inspector.text'), textRows(n)),
|
|
1203
|
+
section(t('inspector.shownWhen'), n.conditions.map((c) => h('div', { class: 'row' }, [
|
|
1170
1204
|
h('b', {}, [`${c.kind} `]),
|
|
1171
1205
|
c.detail,
|
|
1172
1206
|
c.location ? ` ${c.location.file}:${c.location.line}` : '',
|
|
1173
1207
|
]))),
|
|
1174
|
-
section('
|
|
1208
|
+
section(t('inspector.behaviour'), n.events.map((e) => h('div', { class: 'row' }, [
|
|
1175
1209
|
h('span', { class: 'chip red' }, [`on ${e.dom}`]),
|
|
1176
1210
|
h('span', { class: 'chip' }, [e.event]),
|
|
1177
1211
|
...e.transitions.map((t) => h('span', { class: 'chip' }, [`${t.from} → ${t.to}${t.guarded ? ' if …' : ''}`])),
|
|
1178
1212
|
]))),
|
|
1179
|
-
section('
|
|
1213
|
+
section(t('inspector.insideIt'), [
|
|
1180
1214
|
children.length
|
|
1181
1215
|
? h('div', { class: 'kids' }, children.map((c) => h('button', {
|
|
1182
1216
|
type: 'button',
|
|
@@ -1184,9 +1218,11 @@ function developerSections(n, active) {
|
|
|
1184
1218
|
}, [c.kind === 'text' ? `text ${c.text}` : `${c.kind} ${c.id.split('/').slice(-1)[0]}`])))
|
|
1185
1219
|
: null,
|
|
1186
1220
|
h('div', { class: 'nav' }, [
|
|
1187
|
-
h('button', { type: 'button', onclick: () => walk('up') }, ['
|
|
1188
|
-
isText(n)
|
|
1189
|
-
|
|
1221
|
+
h('button', { type: 'button', onclick: () => walk('up') }, [t('inspector.parent')]),
|
|
1222
|
+
isText(n) || n.component?.client
|
|
1223
|
+
? null
|
|
1224
|
+
: h('button', { type: 'button', onclick: () => walk('down') }, [t('inspector.child')]),
|
|
1225
|
+
h('button', { type: 'button', onclick: removeActive }, [t('inspector.remove')]),
|
|
1190
1226
|
]),
|
|
1191
1227
|
]),
|
|
1192
1228
|
h('div', { class: 'sec meta' }, [`node ${n.id}`, n.pointer ? ` · ${n.pointer}` : '']),
|
|
@@ -1212,19 +1248,13 @@ async function renderPanel() {
|
|
|
1212
1248
|
if (state.panel !== 'inspector' || !active) {
|
|
1213
1249
|
panel.hidden = state.view !== 'workbench';
|
|
1214
1250
|
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
|
-
]));
|
|
1251
|
+
panel.replaceChildren(h('div', { class: 'empty' }, [h('b', {}, [t('inspector.empty.title')]), t('inspector.empty.text')]));
|
|
1219
1252
|
return;
|
|
1220
1253
|
}
|
|
1221
1254
|
const n = await node(active.id);
|
|
1222
1255
|
if (!n) {
|
|
1223
1256
|
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
|
-
]));
|
|
1257
|
+
panel.replaceChildren(h('div', { class: 'empty' }, [h('b', {}, [t('inspector.gone.title')]), t('inspector.gone.text')]));
|
|
1228
1258
|
return;
|
|
1229
1259
|
}
|
|
1230
1260
|
const nodes = await Promise.all(state.picks.map((p) => node(p.id)));
|
|
@@ -1269,8 +1299,8 @@ function tabs(current, saved) {
|
|
|
1269
1299
|
onclick: () => open('changes', null, name),
|
|
1270
1300
|
}, [label]);
|
|
1271
1301
|
return h('div', { class: 'tabs' }, [
|
|
1272
|
-
tab('draft',
|
|
1273
|
-
tab('saved',
|
|
1302
|
+
tab('draft', t('changes.tab.draft', { count: drafted().length })),
|
|
1303
|
+
tab('saved', t('changes.tab.saved', { count: saved })),
|
|
1274
1304
|
]);
|
|
1275
1305
|
}
|
|
1276
1306
|
async function renderChanges() {
|
|
@@ -1278,8 +1308,8 @@ async function renderChanges() {
|
|
|
1278
1308
|
const current = state.tab;
|
|
1279
1309
|
panel.hidden = false;
|
|
1280
1310
|
const head = h('div', { class: 'head' }, [
|
|
1281
|
-
h('div', { class: 'kicker' }, [current === 'draft' ? '
|
|
1282
|
-
h('h2', { class: 'title' }, ['
|
|
1311
|
+
h('div', { class: 'kicker' }, [current === 'draft' ? t('changes.notSent') : '.hozu/requests']),
|
|
1312
|
+
h('h2', { class: 'title' }, [t('changes.title')]),
|
|
1283
1313
|
closeButton(() => open(state.picks.length ? 'inspector' : null)),
|
|
1284
1314
|
tabs(current, list.length),
|
|
1285
1315
|
]);
|
|
@@ -1292,17 +1322,17 @@ async function renderChanges() {
|
|
|
1292
1322
|
const done = h('button', {
|
|
1293
1323
|
class: 'link done',
|
|
1294
1324
|
type: 'button',
|
|
1295
|
-
title: '
|
|
1325
|
+
title: t('changes.resolve.title'),
|
|
1296
1326
|
onclick: async () => {
|
|
1297
1327
|
if (!armed) {
|
|
1298
1328
|
armed = true;
|
|
1299
|
-
done.textContent = '
|
|
1329
|
+
done.textContent = t('changes.resolve.confirm');
|
|
1300
1330
|
return;
|
|
1301
1331
|
}
|
|
1302
1332
|
await remove(r.number);
|
|
1303
1333
|
void renderPanel();
|
|
1304
1334
|
},
|
|
1305
|
-
}, ['
|
|
1335
|
+
}, [t('changes.resolve')]);
|
|
1306
1336
|
return h('div', { class: 'req' }, [
|
|
1307
1337
|
h('span', { class: 'n' }, [r.number]),
|
|
1308
1338
|
h('button', { class: 't open-req', type: 'button', onclick: () => open('changes', r.number, 'saved') }, [r.title]),
|
|
@@ -1315,7 +1345,7 @@ async function renderChanges() {
|
|
|
1315
1345
|
message ? h('div', { class: 'notice' }, message) : null,
|
|
1316
1346
|
...(list.length
|
|
1317
1347
|
? [...list].reverse().map(row)
|
|
1318
|
-
: [h('div', { class: 'empty' }, ['
|
|
1348
|
+
: [h('div', { class: 'empty' }, [t('changes.noRequests')])]),
|
|
1319
1349
|
list.length
|
|
1320
1350
|
? h('div', { class: 'sec' }, [
|
|
1321
1351
|
h('div', { class: 'choice' }, [
|
|
@@ -1325,24 +1355,18 @@ async function renderChanges() {
|
|
|
1325
1355
|
onclick: async () => {
|
|
1326
1356
|
const full = await Promise.all(list.map((r) => one(r.number)));
|
|
1327
1357
|
await copy(joinRequests(full));
|
|
1328
|
-
status(result, 'ok', [
|
|
1329
|
-
`Copied all ${list.length} as one prompt. Paste it to your agent.`,
|
|
1330
|
-
]);
|
|
1358
|
+
status(result, 'ok', [t('changes.copiedAll', { count: list.length })]);
|
|
1331
1359
|
},
|
|
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
|
-
]),
|
|
1360
|
+
}, [t('changes.copyAll', { count: list.length })]),
|
|
1361
|
+
h('span', { class: 'why' }, [t('changes.copyAll.why')]),
|
|
1336
1362
|
h('button', {
|
|
1337
1363
|
type: 'button',
|
|
1338
1364
|
onclick: async () => {
|
|
1339
1365
|
await copy(openRequestsLine(list));
|
|
1340
|
-
status(result, 'ok', ['
|
|
1366
|
+
status(result, 'ok', [t('changes.copiedPaths')]);
|
|
1341
1367
|
},
|
|
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
|
-
]),
|
|
1368
|
+
}, [t('changes.copyPaths')]),
|
|
1369
|
+
h('span', { class: 'why' }, [t('changes.copyPaths.why')]),
|
|
1346
1370
|
]),
|
|
1347
1371
|
result,
|
|
1348
1372
|
])
|
|
@@ -1365,7 +1389,9 @@ async function renderChanges() {
|
|
|
1365
1389
|
[
|
|
1366
1390
|
p.note.trim(),
|
|
1367
1391
|
p.style?.length
|
|
1368
|
-
?
|
|
1392
|
+
? t(p.style.length === 1 ? 'changes.styles.one' : 'changes.styles.other', {
|
|
1393
|
+
count: p.style.length,
|
|
1394
|
+
})
|
|
1369
1395
|
: '',
|
|
1370
1396
|
]
|
|
1371
1397
|
.filter(Boolean)
|
|
@@ -1379,7 +1405,7 @@ async function renderChanges() {
|
|
|
1379
1405
|
state.active = state.picks.indexOf(p);
|
|
1380
1406
|
open('inspector');
|
|
1381
1407
|
},
|
|
1382
|
-
}, ['
|
|
1408
|
+
}, [t('changes.edit')]),
|
|
1383
1409
|
h('button', {
|
|
1384
1410
|
class: 'link',
|
|
1385
1411
|
type: 'button',
|
|
@@ -1393,13 +1419,10 @@ async function renderChanges() {
|
|
|
1393
1419
|
renderDock();
|
|
1394
1420
|
void renderPanel();
|
|
1395
1421
|
},
|
|
1396
|
-
}, ['
|
|
1422
|
+
}, [t('changes.remove')]),
|
|
1397
1423
|
]);
|
|
1398
1424
|
}))
|
|
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
|
-
]),
|
|
1425
|
+
: h('div', { class: 'empty' }, [h('b', {}, [t('changes.empty.title')]), t('changes.empty.text')]),
|
|
1403
1426
|
items.length ? h('div', { class: 'sec' }, [exportBlock()]) : null,
|
|
1404
1427
|
]));
|
|
1405
1428
|
}
|
|
@@ -1420,17 +1443,17 @@ async function renderRequest(number) {
|
|
|
1420
1443
|
onclick: async () => {
|
|
1421
1444
|
if (!armed) {
|
|
1422
1445
|
armed = true;
|
|
1423
|
-
del.textContent = '
|
|
1446
|
+
del.textContent = t('request.remove.confirm');
|
|
1424
1447
|
return;
|
|
1425
1448
|
}
|
|
1426
1449
|
await remove(r.number);
|
|
1427
1450
|
renderDock();
|
|
1428
1451
|
open('changes', null, 'saved');
|
|
1429
1452
|
},
|
|
1430
|
-
}, ['
|
|
1453
|
+
}, [t('request.remove')]);
|
|
1431
1454
|
panel.replaceChildren(h('div', { class: 'head' }, [
|
|
1432
1455
|
h('button', { class: 'back', type: 'button', onclick: () => open('changes', null, 'saved') }, [
|
|
1433
|
-
'
|
|
1456
|
+
t('request.back'),
|
|
1434
1457
|
]),
|
|
1435
1458
|
h('h2', { class: 'title' }, [r.title]),
|
|
1436
1459
|
h('div', { class: 'kicker' }, [r.file]),
|
|
@@ -1442,16 +1465,16 @@ async function renderRequest(number) {
|
|
|
1442
1465
|
type: 'button',
|
|
1443
1466
|
onclick: async () => {
|
|
1444
1467
|
await copy(`Do the Hozu request ${r.file}`);
|
|
1445
|
-
status(result, 'ok', [`
|
|
1468
|
+
status(result, 'ok', [t('request.copiedAsk', { ask: `Do the Hozu request ${r.file}` })]);
|
|
1446
1469
|
},
|
|
1447
|
-
}, ['
|
|
1470
|
+
}, [t('request.copyAsk')]),
|
|
1448
1471
|
h('button', {
|
|
1449
1472
|
type: 'button',
|
|
1450
1473
|
onclick: async () => {
|
|
1451
1474
|
await copy(r.markdown);
|
|
1452
|
-
status(result, 'ok', ['
|
|
1475
|
+
status(result, 'ok', [t('request.copied')]);
|
|
1453
1476
|
},
|
|
1454
|
-
}, ['
|
|
1477
|
+
}, [t('request.copy')]),
|
|
1455
1478
|
del,
|
|
1456
1479
|
]),
|
|
1457
1480
|
result,
|
|
@@ -1475,14 +1498,14 @@ function renderSettings() {
|
|
|
1475
1498
|
]);
|
|
1476
1499
|
panel.replaceChildren(h('div', { class: 'head' }, [
|
|
1477
1500
|
h('div', { class: 'kicker' }, ['Hozu DevTools']),
|
|
1478
|
-
h('h2', { class: 'title' }, ['
|
|
1501
|
+
h('h2', { class: 'title' }, [t('settings.title')]),
|
|
1479
1502
|
closeButton(() => open(state.picks.length ? 'inspector' : null)),
|
|
1480
1503
|
]), h('div', { class: 'sec' }, [
|
|
1481
|
-
h('div', { class: 'label' }, ['
|
|
1482
|
-
h('div', { class: 'tabs', role: 'radiogroup', 'aria-label': '
|
|
1483
|
-
['system', '
|
|
1484
|
-
['light', '
|
|
1485
|
-
['dark', '
|
|
1504
|
+
h('div', { class: 'label' }, [t('settings.appearance')]),
|
|
1505
|
+
h('div', { class: 'tabs', role: 'radiogroup', 'aria-label': t('settings.appearance') }, [
|
|
1506
|
+
['system', t('settings.system')],
|
|
1507
|
+
['light', t('settings.light')],
|
|
1508
|
+
['dark', t('settings.dark')],
|
|
1486
1509
|
].map(([value, label]) => h('button', {
|
|
1487
1510
|
class: 'tab',
|
|
1488
1511
|
type: 'button',
|
|
@@ -1497,13 +1520,13 @@ function renderSettings() {
|
|
|
1497
1520
|
},
|
|
1498
1521
|
}, [label]))),
|
|
1499
1522
|
]), h('div', { class: 'sec' }, [
|
|
1500
|
-
h('div', { class: 'label' }, ['
|
|
1523
|
+
h('div', { class: 'label' }, [t('settings.show')]),
|
|
1501
1524
|
h('fieldset', {}, [
|
|
1502
|
-
choice('builder', '
|
|
1503
|
-
choice('developer', '
|
|
1525
|
+
choice('builder', t('settings.builder'), t('settings.builder.detail')),
|
|
1526
|
+
choice('developer', t('settings.developer'), t('settings.developer.detail')),
|
|
1504
1527
|
]),
|
|
1505
1528
|
]), h('div', { class: 'sec' }, [
|
|
1506
|
-
h('div', { class: 'label' }, ['
|
|
1529
|
+
h('div', { class: 'label' }, [t('settings.requests')]),
|
|
1507
1530
|
h('label', { class: 'option' }, [
|
|
1508
1531
|
h('input', {
|
|
1509
1532
|
type: 'checkbox',
|
|
@@ -1513,26 +1536,21 @@ function renderSettings() {
|
|
|
1513
1536
|
persist();
|
|
1514
1537
|
},
|
|
1515
1538
|
}),
|
|
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
|
-
]),
|
|
1539
|
+
h('span', {}, [h('b', {}, [t('settings.excerpt')]), h('span', {}, [t('settings.excerpt.detail')])]),
|
|
1522
1540
|
]),
|
|
1523
1541
|
]), h('div', { class: 'sec' }, [
|
|
1524
|
-
h('div', { class: 'label' }, ['
|
|
1542
|
+
h('div', { class: 'label' }, [t('settings.keys')]),
|
|
1525
1543
|
...[
|
|
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', '
|
|
1544
|
+
['Alt+Shift+S', t('keys.toggle')],
|
|
1545
|
+
['Click', t('keys.click')],
|
|
1546
|
+
['Shift+click', t('keys.add')],
|
|
1547
|
+
['Shift+Enter', t('keys.around')],
|
|
1548
|
+
['Enter', t('keys.inside')],
|
|
1549
|
+
['Tab / Shift+Tab', t('keys.beside')],
|
|
1550
|
+
['Alt (hold)', t('keys.measure')],
|
|
1551
|
+
['Double-click', t('keys.text')],
|
|
1552
|
+
['↑ ↓', t('keys.arrows')],
|
|
1553
|
+
['Esc', t('keys.escape')],
|
|
1536
1554
|
].map(([k, v]) => h('div', { class: 'keyrow' }, [h('kbd', {}, [k]), v])),
|
|
1537
1555
|
]));
|
|
1538
1556
|
}
|
|
@@ -1542,6 +1560,12 @@ const devices = [
|
|
|
1542
1560
|
{ name: 'Laptop', width: 1280, height: 800 },
|
|
1543
1561
|
{ name: 'Desktop', width: 1440, height: 900 },
|
|
1544
1562
|
];
|
|
1563
|
+
const deviceNames = {
|
|
1564
|
+
Phone: 'bench.phone',
|
|
1565
|
+
Tablet: 'bench.tablet',
|
|
1566
|
+
Laptop: 'bench.laptop',
|
|
1567
|
+
Desktop: 'bench.desktop',
|
|
1568
|
+
};
|
|
1545
1569
|
const left = h('div', { class: 'bench-left' });
|
|
1546
1570
|
const stage = h('div', { class: 'stage' });
|
|
1547
1571
|
const sizer = h('div', { class: 'sizer' });
|
|
@@ -1595,21 +1619,23 @@ function renderBar() {
|
|
|
1595
1619
|
onclick: () => setMode(value),
|
|
1596
1620
|
}, [label]);
|
|
1597
1621
|
const select = h('select', {
|
|
1598
|
-
'aria-label': '
|
|
1622
|
+
'aria-label': t('bench.device'),
|
|
1599
1623
|
onchange: (e) => {
|
|
1600
1624
|
const d = devices.find((x) => x.name === e.target.value);
|
|
1601
1625
|
if (d)
|
|
1602
1626
|
setDevice(d);
|
|
1603
1627
|
},
|
|
1604
1628
|
}, [
|
|
1605
|
-
...devices.map((d) => h('option', { value: d.name, selected: d.name === state.device.name }, [
|
|
1629
|
+
...devices.map((d) => h('option', { value: d.name, selected: d.name === state.device.name }, [
|
|
1630
|
+
t('bench.option', { name: t(deviceNames[d.name]), width: d.width }),
|
|
1631
|
+
])),
|
|
1606
1632
|
devices.some((d) => d.name === state.device.name)
|
|
1607
1633
|
? null
|
|
1608
|
-
: h('option', { value: 'Custom', selected: true }, ['
|
|
1634
|
+
: h('option', { value: 'Custom', selected: true }, [t('bench.custom')]),
|
|
1609
1635
|
]);
|
|
1610
1636
|
bar.replaceChildren(...present([
|
|
1611
1637
|
h('div', { class: 'grip' }, [h('img', { src: logo, alt: 'Hozu', width: '18', height: '18' })]),
|
|
1612
|
-
h('div', { class: 'seg' }, [mode('
|
|
1638
|
+
h('div', { class: 'seg' }, [mode(t('dock.browse'), 'browse'), mode(t('dock.select'), 'select')]),
|
|
1613
1639
|
h('button', {
|
|
1614
1640
|
class: 'act layers-toggle',
|
|
1615
1641
|
type: 'button',
|
|
@@ -1618,47 +1644,50 @@ function renderBar() {
|
|
|
1618
1644
|
bench.classList.toggle('layers-open');
|
|
1619
1645
|
renderBar();
|
|
1620
1646
|
},
|
|
1621
|
-
}, ['
|
|
1647
|
+
}, [t('dock.layers')]),
|
|
1622
1648
|
select,
|
|
1623
1649
|
h('button', {
|
|
1624
1650
|
class: 'act',
|
|
1625
1651
|
type: 'button',
|
|
1626
|
-
title: '
|
|
1627
|
-
'aria-label': '
|
|
1652
|
+
title: t('bench.rotate'),
|
|
1653
|
+
'aria-label': t('bench.rotate'),
|
|
1628
1654
|
onclick: () => setDevice({ name: state.device.name, width: state.device.height, height: state.device.width }),
|
|
1629
1655
|
}, ['⟲']),
|
|
1630
1656
|
h('span', { class: 'size', 'data-size': true }),
|
|
1631
1657
|
h('span', { class: 'spacer' }),
|
|
1632
1658
|
held()
|
|
1633
1659
|
? h('button', { class: 'previewing', type: 'button', onclick: () => hold(null, reload) }, [
|
|
1634
|
-
'
|
|
1660
|
+
`${t('dock.preview')} · ${t('dock.exit')}`,
|
|
1635
1661
|
])
|
|
1636
1662
|
: null,
|
|
1637
1663
|
h('button', {
|
|
1638
1664
|
class: 'act',
|
|
1639
1665
|
type: 'button',
|
|
1640
1666
|
onclick: () => open(state.panel === 'changes' ? null : 'changes', null, drafted().length ? 'draft' : 'saved'),
|
|
1641
|
-
}, [
|
|
1667
|
+
}, [
|
|
1668
|
+
t('dock.changes'),
|
|
1669
|
+
h('span', { class: 'count' }, [drafted().length ? String(drafted().length) : '']),
|
|
1670
|
+
]),
|
|
1642
1671
|
h('button', {
|
|
1643
1672
|
class: 'act gear',
|
|
1644
1673
|
type: 'button',
|
|
1645
|
-
'aria-label': '
|
|
1674
|
+
'aria-label': t('dock.settings'),
|
|
1646
1675
|
onclick: () => open(state.panel === 'settings' ? null : 'settings'),
|
|
1647
1676
|
}, ['⚙']),
|
|
1648
1677
|
h('button', {
|
|
1649
1678
|
class: 'act',
|
|
1650
1679
|
type: 'button',
|
|
1651
|
-
title: '
|
|
1680
|
+
title: t('bench.api.title'),
|
|
1652
1681
|
'aria-pressed': api.isOpen() ? 'true' : 'false',
|
|
1653
1682
|
onclick: () => api.toggle(),
|
|
1654
|
-
}, ['
|
|
1683
|
+
}, [t('dock.api')]),
|
|
1655
1684
|
h('button', {
|
|
1656
1685
|
class: 'act',
|
|
1657
1686
|
type: 'button',
|
|
1658
|
-
title: '
|
|
1687
|
+
title: t('dock.assets.title'),
|
|
1659
1688
|
onclick: () => void assets.open(),
|
|
1660
|
-
}, ['
|
|
1661
|
-
h('button', { class: 'act exit', type: 'button', onclick: () => closeBench() }, ['
|
|
1689
|
+
}, [t('dock.assets')]),
|
|
1690
|
+
h('button', { class: 'act exit', type: 'button', onclick: () => closeBench() }, [t('bench.exit')]),
|
|
1662
1691
|
]));
|
|
1663
1692
|
}
|
|
1664
1693
|
async function renderLeft() {
|
|
@@ -1675,7 +1704,7 @@ function openBench() {
|
|
|
1675
1704
|
bench.hidden = false;
|
|
1676
1705
|
const frame = h('iframe', {
|
|
1677
1706
|
src: location.href,
|
|
1678
|
-
title: '
|
|
1707
|
+
title: t('bench.frame'),
|
|
1679
1708
|
'data-hozu-bench': true,
|
|
1680
1709
|
});
|
|
1681
1710
|
frame.addEventListener('load', () => {
|
|
@@ -1686,7 +1715,7 @@ function openBench() {
|
|
|
1686
1715
|
void renderLeft();
|
|
1687
1716
|
void renderPanel();
|
|
1688
1717
|
});
|
|
1689
|
-
const grip = h('div', { class: 'resize', title: '
|
|
1718
|
+
const grip = h('div', { class: 'resize', title: t('common.resize'), 'aria-hidden': 'true' });
|
|
1690
1719
|
grip.addEventListener('pointerdown', (event) => {
|
|
1691
1720
|
event.preventDefault();
|
|
1692
1721
|
grip.setPointerCapture(event.pointerId);
|