@syntrologie/adapt-nav 2.13.0 → 2.15.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/NavWidgetLit.d.ts +56 -0
- package/dist/NavWidgetLit.d.ts.map +1 -0
- package/dist/NavWidgetLit.js +495 -0
- package/dist/NavWidgetLit.test.d.ts +8 -0
- package/dist/NavWidgetLit.test.d.ts.map +1 -0
- package/dist/NavWidgetLit.test.js +199 -0
- package/dist/editor-lit.d.ts +49 -0
- package/dist/editor-lit.d.ts.map +1 -0
- package/dist/editor-lit.js +319 -0
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +3 -3
- package/dist/runtime-lit.d.ts +108 -0
- package/dist/runtime-lit.d.ts.map +1 -0
- package/dist/runtime-lit.js +241 -0
- package/dist/runtime.d.ts +15 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +29 -0
- package/dist/schema.d.ts +216 -216
- package/dist/schema.d.ts.map +1 -1
- package/node_modules/@syntro/design-system/dist/tokens/index.d.ts +2 -0
- package/node_modules/@syntro/design-system/dist/tokens/index.d.ts.map +1 -1
- package/node_modules/@syntro/design-system/dist/tokens/index.js +2 -0
- package/node_modules/@syntro/design-system/dist/tokens/panel-shell.d.ts +93 -0
- package/node_modules/@syntro/design-system/dist/tokens/panel-shell.d.ts.map +1 -0
- package/node_modules/@syntro/design-system/dist/tokens/panel-shell.js +72 -0
- package/node_modules/@syntrologie/sdk-contracts/dist/index.d.ts +1 -1
- package/node_modules/@syntrologie/sdk-contracts/dist/index.js +5 -3
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.d.ts +150 -79
- package/node_modules/@syntrologie/sdk-contracts/dist/schemas.js +266 -67
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.js +6 -3
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPickerLit.d.ts +84 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPickerLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPickerLit.js +323 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggleLit.d.ts +25 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggleLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggleLit.js +55 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLineLit.d.ts +33 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLineLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLineLit.js +118 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadgeLit.d.ts +32 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadgeLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadgeLit.js +68 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSectionLit.d.ts +34 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSectionLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSectionLit.js +57 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButtonLit.d.ts +13 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButtonLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButtonLit.js +31 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBodyLit.d.ts +7 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBodyLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBodyLit.js +15 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCardLit.d.ts +36 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCardLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCardLit.js +102 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooterLit.d.ts +20 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooterLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooterLit.js +48 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeaderLit.d.ts +16 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeaderLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeaderLit.js +25 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInputLit.d.ts +66 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInputLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInputLit.js +87 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayoutLit.d.ts +7 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayoutLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayoutLit.js +15 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.js +28 -17
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShellLit.d.ts +66 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShellLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShellLit.js +528 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelectLit.d.ts +41 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelectLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelectLit.js +63 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextareaLit.d.ts +55 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextareaLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextareaLit.js +92 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlightLit.d.ts +90 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlightLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlightLit.js +242 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyStateLit.d.ts +12 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyStateLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyStateLit.js +21 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeaderLit.d.ts +21 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeaderLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeaderLit.js +33 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourneyLit.d.ts +28 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourneyLit.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourneyLit.js +121 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/controllers/PanelShellController.d.ts +110 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/controllers/PanelShellController.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/controllers/PanelShellController.js +476 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts +2 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/index.js +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/lit-elements.d.ts +15 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/lit-elements.d.ts.map +1 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/lit-elements.js +14 -0
- package/node_modules/@syntrologie/shared-editor-ui/dist/utils/elementChainRecommender.d.ts +0 -4
- package/node_modules/@syntrologie/shared-editor-ui/dist/utils/elementChainRecommender.d.ts.map +1 -1
- package/node_modules/@syntrologie/shared-editor-ui/dist/utils/elementChainRecommender.js +17 -1
- package/node_modules/@syntrologie/shared-editor-ui/package.json +9 -1
- package/package.json +12 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NavWidgetLit — Vitest + @open-wc/testing-helpers unit tests
|
|
3
|
+
*
|
|
4
|
+
* Tests cover: rendering, expand/collapse, nav-tip-clicked event, XSS rejection,
|
|
5
|
+
* empty state, category grouping, and visibility filtering (triggerWhen).
|
|
6
|
+
*/
|
|
7
|
+
import { fixture, html } from '@open-wc/testing-helpers';
|
|
8
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
9
|
+
import { NavWidgetLit } from './NavWidgetLit';
|
|
10
|
+
// ── Custom element registration ───────────────────────────────────────────────
|
|
11
|
+
const TAG = 'syntro-nav-tips';
|
|
12
|
+
if (!customElements.get(TAG)) {
|
|
13
|
+
customElements.define(TAG, NavWidgetLit);
|
|
14
|
+
}
|
|
15
|
+
// ── Helpers ────────────────────────────────────────────────────────────────────
|
|
16
|
+
function makeTip(id, overrides = {}) {
|
|
17
|
+
return {
|
|
18
|
+
kind: 'nav:tip',
|
|
19
|
+
config: {
|
|
20
|
+
id,
|
|
21
|
+
title: `Tip ${id}`,
|
|
22
|
+
description: `Description for tip ${id}`,
|
|
23
|
+
...overrides,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function makeRuntime(overrides = {}) {
|
|
28
|
+
return {
|
|
29
|
+
evaluateSync: vi.fn().mockReturnValue({ value: true, isFallback: false }),
|
|
30
|
+
context: { subscribe: vi.fn().mockReturnValue(vi.fn()) },
|
|
31
|
+
events: { publish: vi.fn() },
|
|
32
|
+
accumulator: {
|
|
33
|
+
subscribe: vi.fn().mockReturnValue(vi.fn()),
|
|
34
|
+
register: vi.fn(),
|
|
35
|
+
},
|
|
36
|
+
...overrides,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function makeConfig(tips, overrides = {}) {
|
|
40
|
+
return {
|
|
41
|
+
expandBehavior: 'single',
|
|
42
|
+
theme: 'light',
|
|
43
|
+
actions: tips,
|
|
44
|
+
...overrides,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async function renderWidget(tips, configOverrides = {}, runtime) {
|
|
48
|
+
const el = await fixture(html `<syntro-nav-tips></syntro-nav-tips>`);
|
|
49
|
+
el.config = makeConfig(tips, configOverrides);
|
|
50
|
+
el.runtime = runtime;
|
|
51
|
+
await el.updateComplete;
|
|
52
|
+
return el;
|
|
53
|
+
}
|
|
54
|
+
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
55
|
+
describe('NavWidgetLit', () => {
|
|
56
|
+
// ── Rendering ────────────────────────────────────────────────────────────
|
|
57
|
+
it('renders navigation tips from config', async () => {
|
|
58
|
+
const el = await renderWidget([makeTip('t1'), makeTip('t2')]);
|
|
59
|
+
expect(el.querySelectorAll('[data-nav-tip-id]')).toHaveLength(2);
|
|
60
|
+
expect(el.querySelector('[data-nav-tip-id="t1"]')).not.toBeNull();
|
|
61
|
+
expect(el.querySelector('[data-nav-tip-id="t2"]')).not.toBeNull();
|
|
62
|
+
});
|
|
63
|
+
it('renders tip titles in button text', async () => {
|
|
64
|
+
const el = await renderWidget([makeTip('a', { title: 'My Nav Tip' })]);
|
|
65
|
+
const btn = el.querySelector('button');
|
|
66
|
+
expect(btn.textContent).toContain('My Nav Tip');
|
|
67
|
+
});
|
|
68
|
+
// ── Expand / collapse ────────────────────────────────────────────────────
|
|
69
|
+
it('starts with all tips collapsed', async () => {
|
|
70
|
+
const el = await renderWidget([makeTip('t1'), makeTip('t2')]);
|
|
71
|
+
const buttons = el.querySelectorAll('button[aria-expanded]');
|
|
72
|
+
for (const btn of Array.from(buttons)) {
|
|
73
|
+
expect(btn.getAttribute('aria-expanded')).toBe('false');
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
it('expands a tip when its header button is clicked', async () => {
|
|
77
|
+
const el = await renderWidget([makeTip('t1')]);
|
|
78
|
+
const btn = el.querySelector('[data-nav-tip-id="t1"] button');
|
|
79
|
+
btn.click();
|
|
80
|
+
await el.updateComplete;
|
|
81
|
+
expect(btn.getAttribute('aria-expanded')).toBe('true');
|
|
82
|
+
});
|
|
83
|
+
it('collapses an expanded tip on second click', async () => {
|
|
84
|
+
const el = await renderWidget([makeTip('t1')]);
|
|
85
|
+
const btn = el.querySelector('[data-nav-tip-id="t1"] button');
|
|
86
|
+
btn.click();
|
|
87
|
+
await el.updateComplete;
|
|
88
|
+
btn.click();
|
|
89
|
+
await el.updateComplete;
|
|
90
|
+
expect(btn.getAttribute('aria-expanded')).toBe('false');
|
|
91
|
+
});
|
|
92
|
+
it('collapses previously-expanded tip in single-expand mode', async () => {
|
|
93
|
+
const el = await renderWidget([makeTip('t1'), makeTip('t2')], {
|
|
94
|
+
expandBehavior: 'single',
|
|
95
|
+
});
|
|
96
|
+
const btn1 = el.querySelector('[data-nav-tip-id="t1"] button');
|
|
97
|
+
const btn2 = el.querySelector('[data-nav-tip-id="t2"] button');
|
|
98
|
+
btn1.click();
|
|
99
|
+
await el.updateComplete;
|
|
100
|
+
expect(btn1.getAttribute('aria-expanded')).toBe('true');
|
|
101
|
+
btn2.click();
|
|
102
|
+
await el.updateComplete;
|
|
103
|
+
expect(btn1.getAttribute('aria-expanded')).toBe('false');
|
|
104
|
+
expect(btn2.getAttribute('aria-expanded')).toBe('true');
|
|
105
|
+
});
|
|
106
|
+
it('allows multiple tips open simultaneously in multiple-expand mode', async () => {
|
|
107
|
+
const el = await renderWidget([makeTip('t1'), makeTip('t2')], {
|
|
108
|
+
expandBehavior: 'multiple',
|
|
109
|
+
});
|
|
110
|
+
const btn1 = el.querySelector('[data-nav-tip-id="t1"] button');
|
|
111
|
+
const btn2 = el.querySelector('[data-nav-tip-id="t2"] button');
|
|
112
|
+
btn1.click();
|
|
113
|
+
await el.updateComplete;
|
|
114
|
+
btn2.click();
|
|
115
|
+
await el.updateComplete;
|
|
116
|
+
expect(btn1.getAttribute('aria-expanded')).toBe('true');
|
|
117
|
+
expect(btn2.getAttribute('aria-expanded')).toBe('true');
|
|
118
|
+
});
|
|
119
|
+
// ── Custom event ─────────────────────────────────────────────────────────
|
|
120
|
+
it('fires nav-tip-clicked custom event when CTA link is clicked', async () => {
|
|
121
|
+
const tip = makeTip('t1', { href: '/dashboard' });
|
|
122
|
+
const el = await renderWidget([tip]);
|
|
123
|
+
// Expand so the CTA link is rendered
|
|
124
|
+
const btn = el.querySelector('[data-nav-tip-id="t1"] button');
|
|
125
|
+
btn.click();
|
|
126
|
+
await el.updateComplete;
|
|
127
|
+
const events = [];
|
|
128
|
+
el.addEventListener('nav-tip-clicked', (e) => events.push(e));
|
|
129
|
+
const link = el.querySelector('a');
|
|
130
|
+
link.click();
|
|
131
|
+
expect(events).toHaveLength(1);
|
|
132
|
+
expect(events[0].detail.href).toBe('/dashboard');
|
|
133
|
+
});
|
|
134
|
+
// ── XSS rejection ────────────────────────────────────────────────────────
|
|
135
|
+
it('does not navigate when href starts with javascript:', async () => {
|
|
136
|
+
const originalOpen = window.open;
|
|
137
|
+
const openSpy = vi.fn();
|
|
138
|
+
window.open = openSpy;
|
|
139
|
+
// Directly invoke the internal navigate path via a link with a JS URL
|
|
140
|
+
const tip = makeTip('xss', { href: 'javascript:alert(1)', external: false });
|
|
141
|
+
const el = await renderWidget([tip]);
|
|
142
|
+
const headerBtn = el.querySelector('[data-nav-tip-id="xss"] button');
|
|
143
|
+
headerBtn.click();
|
|
144
|
+
await el.updateComplete;
|
|
145
|
+
const link = el.querySelector('a');
|
|
146
|
+
if (link) {
|
|
147
|
+
const navEvents = [];
|
|
148
|
+
el.addEventListener('nav-tip-clicked', (e) => navEvents.push(e));
|
|
149
|
+
link.click();
|
|
150
|
+
// The click handler checks the href and bails out early — no event should fire
|
|
151
|
+
expect(navEvents).toHaveLength(0);
|
|
152
|
+
}
|
|
153
|
+
window.open = originalOpen;
|
|
154
|
+
});
|
|
155
|
+
// ── Empty state ───────────────────────────────────────────────────────────
|
|
156
|
+
it('renders empty state when config has no actions', async () => {
|
|
157
|
+
const el = await renderWidget([]);
|
|
158
|
+
// No tip items
|
|
159
|
+
expect(el.querySelectorAll('[data-nav-tip-id]')).toHaveLength(0);
|
|
160
|
+
// The empty-state message is present
|
|
161
|
+
expect(el.textContent).toContain("You're all set for now");
|
|
162
|
+
});
|
|
163
|
+
// ── Category grouping ─────────────────────────────────────────────────────
|
|
164
|
+
it('renders category headers when tips have categories', async () => {
|
|
165
|
+
const tips = [
|
|
166
|
+
makeTip('t1', { category: 'Getting Started' }),
|
|
167
|
+
makeTip('t2', { category: 'Advanced' }),
|
|
168
|
+
];
|
|
169
|
+
const el = await renderWidget(tips);
|
|
170
|
+
const catHeaders = el.querySelectorAll('[data-category-header]');
|
|
171
|
+
expect(catHeaders).toHaveLength(2);
|
|
172
|
+
const headerTexts = Array.from(catHeaders).map((h) => h.textContent?.trim());
|
|
173
|
+
expect(headerTexts).toContain('Getting Started');
|
|
174
|
+
expect(headerTexts).toContain('Advanced');
|
|
175
|
+
});
|
|
176
|
+
it('does not render category headers when no tips have categories', async () => {
|
|
177
|
+
const tips = [makeTip('t1'), makeTip('t2')];
|
|
178
|
+
const el = await renderWidget(tips);
|
|
179
|
+
expect(el.querySelectorAll('[data-category-header]')).toHaveLength(0);
|
|
180
|
+
});
|
|
181
|
+
// ── Visibility filtering (triggerWhen) ────────────────────────────────────
|
|
182
|
+
it('hides tips whose triggerWhen evaluates to false', async () => {
|
|
183
|
+
const visibleTip = makeTip('visible');
|
|
184
|
+
const hiddenTip = {
|
|
185
|
+
...makeTip('hidden'),
|
|
186
|
+
triggerWhen: { type: 'rules', rules: [], default: false },
|
|
187
|
+
};
|
|
188
|
+
const runtime = makeRuntime({
|
|
189
|
+
evaluateSync: vi.fn((strategy) => {
|
|
190
|
+
if (strategy.default === false)
|
|
191
|
+
return { value: false, isFallback: false };
|
|
192
|
+
return { value: true, isFallback: false };
|
|
193
|
+
}),
|
|
194
|
+
});
|
|
195
|
+
const el = await renderWidget([visibleTip, hiddenTip], {}, runtime);
|
|
196
|
+
expect(el.querySelector('[data-nav-tip-id="visible"]')).not.toBeNull();
|
|
197
|
+
expect(el.querySelector('[data-nav-tip-id="hidden"]')).toBeNull();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Nav - Lit Editor Component
|
|
3
|
+
*
|
|
4
|
+
* Lit web component port of the React Nav editor (editor.tsx).
|
|
5
|
+
* Displays nav tip cards with detection badges, inline editing
|
|
6
|
+
* (Title, Description, Link URL, Icon, Category), and rationale.
|
|
7
|
+
*
|
|
8
|
+
* Custom events:
|
|
9
|
+
* navigate-home — user clicked back
|
|
10
|
+
* dirty-change — { dirty: boolean }
|
|
11
|
+
*/
|
|
12
|
+
import { LitElement } from 'lit';
|
|
13
|
+
import { type NavConfig } from './types';
|
|
14
|
+
export declare class NavEditorLit extends LitElement {
|
|
15
|
+
static properties: {
|
|
16
|
+
config: {
|
|
17
|
+
attribute: boolean;
|
|
18
|
+
};
|
|
19
|
+
onChange: {
|
|
20
|
+
attribute: boolean;
|
|
21
|
+
};
|
|
22
|
+
_editingKey: {
|
|
23
|
+
state: boolean;
|
|
24
|
+
};
|
|
25
|
+
_detectionMap: {
|
|
26
|
+
state: boolean;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
config: NavConfig | null;
|
|
30
|
+
onChange: ((updated: Record<string, unknown>) => void) | null;
|
|
31
|
+
private _editingKey;
|
|
32
|
+
private _detectionMap;
|
|
33
|
+
private _detectionInterval;
|
|
34
|
+
private _onPopstate;
|
|
35
|
+
createRenderRoot(): this;
|
|
36
|
+
connectedCallback(): void;
|
|
37
|
+
disconnectedCallback(): void;
|
|
38
|
+
firstUpdated(): void;
|
|
39
|
+
private _runDetection;
|
|
40
|
+
private _handleBack;
|
|
41
|
+
private _handleItemClick;
|
|
42
|
+
private _handleFieldChange;
|
|
43
|
+
private _handleBadgeClick;
|
|
44
|
+
private _renderDetectionBadge;
|
|
45
|
+
private _renderEditMode;
|
|
46
|
+
private _renderListMode;
|
|
47
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=editor-lit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor-lit.d.ts","sourceRoot":"","sources":["../src/editor-lit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAQ,UAAU,EAAW,MAAM,KAAK,CAAC;AAGhD,OAAO,EAAe,KAAK,SAAS,EAAwC,MAAM,SAAS,CAAC;AAkH5F,qBAAa,YAAa,SAAQ,UAAU;IAC1C,OAAgB,UAAU;;;;;;;;;;;;;MAKxB;IAEF,MAAM,EAAE,SAAS,GAAG,IAAI,CAAQ;IAChC,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAErE,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,kBAAkB,CAA+C;IACzE,OAAO,CAAC,WAAW,CAA8B;IAExC,gBAAgB;IAIhB,iBAAiB,IAAI,IAAI;IAOzB,oBAAoB,IAAI,IAAI;IAS5B,YAAY,IAAI,IAAI;IAK7B,OAAO,CAAC,aAAa,CAGnB;IAIF,OAAO,CAAC,WAAW,CAMjB;IAEF,OAAO,CAAC,gBAAgB,CAEtB;IAEF,OAAO,CAAC,kBAAkB,CAaxB;IAIF,OAAO,CAAC,iBAAiB,CAOvB;IAEF,OAAO,CAAC,qBAAqB,CAkB3B;IAEF,OAAO,CAAC,eAAe,CA0DrB;IAEF,OAAO,CAAC,eAAe,CA0CrB;IAEO,MAAM;CAoChB"}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Nav - Lit Editor Component
|
|
3
|
+
*
|
|
4
|
+
* Lit web component port of the React Nav editor (editor.tsx).
|
|
5
|
+
* Displays nav tip cards with detection badges, inline editing
|
|
6
|
+
* (Title, Description, Link URL, Icon, Category), and rationale.
|
|
7
|
+
*
|
|
8
|
+
* Custom events:
|
|
9
|
+
* navigate-home — user clicked back
|
|
10
|
+
* dirty-change — { dirty: boolean }
|
|
11
|
+
*/
|
|
12
|
+
import { html, LitElement, nothing } from 'lit';
|
|
13
|
+
import { describeTrigger, summarizeNavItem } from './summarize';
|
|
14
|
+
import { isOwnAction } from './types';
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Targeting Extraction
|
|
17
|
+
// ============================================================================
|
|
18
|
+
const isRuleStrategy = (s) => typeof s === 'object' &&
|
|
19
|
+
s !== null &&
|
|
20
|
+
s.type === 'rules' &&
|
|
21
|
+
Array.isArray(s.rules);
|
|
22
|
+
const extractFirstPage = (triggerWhen) => {
|
|
23
|
+
if (!triggerWhen || !isRuleStrategy(triggerWhen))
|
|
24
|
+
return null;
|
|
25
|
+
for (const rule of triggerWhen.rules) {
|
|
26
|
+
for (const cond of rule.conditions) {
|
|
27
|
+
const c = cond;
|
|
28
|
+
if (c.type === 'page_url' && typeof c.url === 'string')
|
|
29
|
+
return c.url;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
};
|
|
34
|
+
const extractFirstAnchor = (triggerWhen) => {
|
|
35
|
+
if (!triggerWhen || !isRuleStrategy(triggerWhen))
|
|
36
|
+
return null;
|
|
37
|
+
for (const rule of triggerWhen.rules) {
|
|
38
|
+
for (const cond of rule.conditions) {
|
|
39
|
+
const c = cond;
|
|
40
|
+
if (c.type === 'anchor_visible' && typeof c.anchorId === 'string')
|
|
41
|
+
return c.anchorId;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
};
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// Helpers
|
|
48
|
+
// ============================================================================
|
|
49
|
+
const flattenItems = (config) => {
|
|
50
|
+
const actions = (config.actions || []).filter(isOwnAction);
|
|
51
|
+
return actions.map((tip, i) => {
|
|
52
|
+
// Prefer the tip's own anchor (real pipeline data ships the target here).
|
|
53
|
+
// Fall back to an `anchor_visible` condition inside triggerWhen for older configs.
|
|
54
|
+
const tipAnchor = tip.kind === 'nav:tip' ? tip.config.anchor?.selector : undefined;
|
|
55
|
+
const rawRoute = tip.kind === 'nav:tip' ? tip.config.anchor?.route : undefined;
|
|
56
|
+
const tipRoute = typeof rawRoute === 'string' ? rawRoute : undefined;
|
|
57
|
+
return {
|
|
58
|
+
key: String(i),
|
|
59
|
+
index: i,
|
|
60
|
+
summary: summarizeNavItem(tip),
|
|
61
|
+
trigger: describeTrigger(tip.triggerWhen),
|
|
62
|
+
rationale: tip.rationale?.why,
|
|
63
|
+
firstAnchor: tipAnchor || extractFirstAnchor(tip.triggerWhen),
|
|
64
|
+
firstPage: tipRoute || extractFirstPage(tip.triggerWhen),
|
|
65
|
+
tip,
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
const runDetection = (items) => {
|
|
70
|
+
const map = new Map();
|
|
71
|
+
const currentPath = window.location.pathname;
|
|
72
|
+
for (const item of items) {
|
|
73
|
+
// Page match
|
|
74
|
+
let pageMatch = true;
|
|
75
|
+
if (item.firstPage) {
|
|
76
|
+
const pattern = item.firstPage;
|
|
77
|
+
const regex = new RegExp(`^${pattern.replace(/\*\*/g, '.*').replace(/(?<!\.)(\*)/g, '[^/]*')}$`);
|
|
78
|
+
pageMatch = regex.test(currentPath);
|
|
79
|
+
}
|
|
80
|
+
// Anchor match
|
|
81
|
+
let anchorFound = false;
|
|
82
|
+
if (item.firstAnchor) {
|
|
83
|
+
try {
|
|
84
|
+
anchorFound = document.querySelector(item.firstAnchor) !== null;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Invalid selector
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
anchorFound = pageMatch;
|
|
92
|
+
}
|
|
93
|
+
map.set(item.key, { found: pageMatch && anchorFound });
|
|
94
|
+
}
|
|
95
|
+
return map;
|
|
96
|
+
};
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// Element
|
|
99
|
+
// ============================================================================
|
|
100
|
+
export class NavEditorLit extends LitElement {
|
|
101
|
+
constructor() {
|
|
102
|
+
super(...arguments);
|
|
103
|
+
this.config = null;
|
|
104
|
+
this.onChange = null;
|
|
105
|
+
this._editingKey = null;
|
|
106
|
+
this._detectionMap = new Map();
|
|
107
|
+
this._detectionInterval = null;
|
|
108
|
+
this._onPopstate = () => this._runDetection();
|
|
109
|
+
this._runDetection = () => {
|
|
110
|
+
if (!this.config)
|
|
111
|
+
return;
|
|
112
|
+
this._detectionMap = runDetection(flattenItems(this.config));
|
|
113
|
+
};
|
|
114
|
+
// ---- Actions ----
|
|
115
|
+
this._handleBack = () => {
|
|
116
|
+
if (this._editingKey !== null) {
|
|
117
|
+
this._editingKey = null;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this.dispatchEvent(new CustomEvent('navigate-home', { bubbles: true }));
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
this._handleItemClick = (key) => {
|
|
124
|
+
this._editingKey = key;
|
|
125
|
+
};
|
|
126
|
+
this._handleFieldChange = (index, field, value) => {
|
|
127
|
+
if (!this.config || !this.onChange)
|
|
128
|
+
return;
|
|
129
|
+
const ownActions = (this.config.actions || []).filter(isOwnAction).slice();
|
|
130
|
+
const tip = { ...ownActions[index], config: { ...ownActions[index].config } };
|
|
131
|
+
tip.config[field] = value;
|
|
132
|
+
ownActions[index] = tip;
|
|
133
|
+
const otherActions = (this.config.actions || []).filter((a) => !isOwnAction(a));
|
|
134
|
+
const updated = { ...this.config, actions: [...otherActions, ...ownActions] };
|
|
135
|
+
this.onChange(updated);
|
|
136
|
+
this.dispatchEvent(new CustomEvent('dirty-change', { detail: { dirty: true }, bubbles: true }));
|
|
137
|
+
};
|
|
138
|
+
// ---- Render ----
|
|
139
|
+
this._handleBadgeClick = (item, e) => {
|
|
140
|
+
e.stopPropagation();
|
|
141
|
+
const href = item.tip.config.href;
|
|
142
|
+
if (!href)
|
|
143
|
+
return;
|
|
144
|
+
// SPA navigate via pushState + popstate
|
|
145
|
+
window.history.pushState({}, '', href);
|
|
146
|
+
window.dispatchEvent(new PopStateEvent('popstate'));
|
|
147
|
+
};
|
|
148
|
+
this._renderDetectionBadge = (item) => {
|
|
149
|
+
const detection = this._detectionMap.get(item.key);
|
|
150
|
+
const found = detection?.found ?? false;
|
|
151
|
+
if (found) {
|
|
152
|
+
return html `<button type="button"
|
|
153
|
+
title="Click to scroll to element"
|
|
154
|
+
@click=${(e) => this._handleBadgeClick(item, e)}
|
|
155
|
+
class="se-shrink-0 se-w-2 se-h-2 se-rounded-full se-border-none se-cursor-pointer"
|
|
156
|
+
style="background:#22c55e"
|
|
157
|
+
></button>`;
|
|
158
|
+
}
|
|
159
|
+
return html `<button type="button"
|
|
160
|
+
title="Click to navigate to page"
|
|
161
|
+
@click=${(e) => this._handleBadgeClick(item, e)}
|
|
162
|
+
class="se-shrink-0 se-w-2 se-h-2 se-rounded-full se-border-none se-cursor-pointer"
|
|
163
|
+
style="background:#ef4444"
|
|
164
|
+
></button>`;
|
|
165
|
+
};
|
|
166
|
+
this._renderEditMode = (item) => {
|
|
167
|
+
const tip = item.tip;
|
|
168
|
+
return html `
|
|
169
|
+
<div class="se-py-1">
|
|
170
|
+
<div class="se-flex se-items-center se-gap-2 se-mb-3 se-text-[13px] se-font-semibold se-text-text-primary">
|
|
171
|
+
<span>🧭</span>
|
|
172
|
+
<span>${item.summary}</span>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<div class="se-mb-3">
|
|
176
|
+
<label class="se-text-[11px] se-font-semibold se-text-text-secondary se-mb-1 se-block">Title</label>
|
|
177
|
+
<input type="text" .value=${tip.config.title}
|
|
178
|
+
@input=${(e) => this._handleFieldChange(item.index, 'title', e.target.value)}
|
|
179
|
+
class="se-w-full se-p-2 se-rounded se-border se-border-border-primary se-bg-input-field-bg se-text-text-primary se-text-sm" />
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<div class="se-mb-3">
|
|
183
|
+
<label class="se-text-[11px] se-font-semibold se-text-text-secondary se-mb-1 se-block">Description</label>
|
|
184
|
+
<input type="text" .value=${tip.config.description}
|
|
185
|
+
@input=${(e) => this._handleFieldChange(item.index, 'description', e.target.value)}
|
|
186
|
+
class="se-w-full se-p-2 se-rounded se-border se-border-border-primary se-bg-input-field-bg se-text-text-primary se-text-sm" />
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<div class="se-mb-3">
|
|
190
|
+
<label class="se-text-[11px] se-font-semibold se-text-text-secondary se-mb-1 se-block">Link URL</label>
|
|
191
|
+
<input type="text" .value=${tip.config.href || ''} placeholder="Optional"
|
|
192
|
+
@input=${(e) => this._handleFieldChange(item.index, 'href', e.target.value)}
|
|
193
|
+
class="se-w-full se-p-2 se-rounded se-border se-border-border-primary se-bg-input-field-bg se-text-text-primary se-text-sm" />
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<div class="se-mb-3">
|
|
197
|
+
<label class="se-text-[11px] se-font-semibold se-text-text-secondary se-mb-1 se-block">Icon</label>
|
|
198
|
+
<input type="text" .value=${tip.config.icon || ''} placeholder="e.g., 🧭"
|
|
199
|
+
@input=${(e) => this._handleFieldChange(item.index, 'icon', e.target.value)}
|
|
200
|
+
class="se-w-full se-p-2 se-rounded se-border se-border-border-primary se-bg-input-field-bg se-text-text-primary se-text-sm" />
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<div class="se-mb-3">
|
|
204
|
+
<label class="se-text-[11px] se-font-semibold se-text-text-secondary se-mb-1 se-block">Category</label>
|
|
205
|
+
<input type="text" .value=${tip.config.category || ''} placeholder="Optional"
|
|
206
|
+
@input=${(e) => this._handleFieldChange(item.index, 'category', e.target.value)}
|
|
207
|
+
class="se-w-full se-p-2 se-rounded se-border se-border-border-primary se-bg-input-field-bg se-text-text-primary se-text-sm" />
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
${tip.rationale
|
|
211
|
+
? html `
|
|
212
|
+
<div>
|
|
213
|
+
<span class="se-text-[11px] se-font-semibold se-text-text-secondary se-mb-1 se-block">AI Rationale</span>
|
|
214
|
+
<div class="se-p-2 se-rounded se-border se-border-dashed se-border-border-primary se-bg-card-bg se-text-text-secondary se-text-xs se-mb-2">
|
|
215
|
+
${tip.rationale.why}
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
`
|
|
219
|
+
: nothing}
|
|
220
|
+
</div>
|
|
221
|
+
`;
|
|
222
|
+
};
|
|
223
|
+
this._renderListMode = (items) => {
|
|
224
|
+
if (items.length === 0) {
|
|
225
|
+
return html `<div class="se-text-center se-py-8 se-px-4 se-text-text-secondary se-text-sm">
|
|
226
|
+
No tips configured.
|
|
227
|
+
</div>`;
|
|
228
|
+
}
|
|
229
|
+
return html `
|
|
230
|
+
<div class="se-text-[11px] se-font-semibold se-uppercase se-tracking-wider se-text-text-secondary se-mb-2 se-px-1">
|
|
231
|
+
TIPS <span class="se-text-text-tertiary se-font-normal">${items.length}</span>
|
|
232
|
+
</div>
|
|
233
|
+
${items.map((item) => html `
|
|
234
|
+
<div
|
|
235
|
+
data-item-key=${item.key}
|
|
236
|
+
@click=${() => this._handleItemClick(item.key)}
|
|
237
|
+
class="se-p-3 se-rounded-lg se-border se-border-border-primary se-bg-card-bg se-cursor-pointer se-mb-2 se-transition-all hover:se-border-pink-4/40 hover:se-bg-pink-4/5"
|
|
238
|
+
>
|
|
239
|
+
<div class="se-flex se-items-center se-gap-2">
|
|
240
|
+
${this._renderDetectionBadge(item)}
|
|
241
|
+
<span class="se-flex-1 se-overflow-hidden se-text-ellipsis se-whitespace-nowrap se-text-sm se-text-text-primary">
|
|
242
|
+
${item.summary}
|
|
243
|
+
</span>
|
|
244
|
+
</div>
|
|
245
|
+
${item.trigger !== 'All pages'
|
|
246
|
+
? html `
|
|
247
|
+
<div class="se-text-[10px] se-text-text-tertiary se-mt-1">📍 ${item.trigger}</div>
|
|
248
|
+
`
|
|
249
|
+
: nothing}
|
|
250
|
+
${item.rationale
|
|
251
|
+
? html `
|
|
252
|
+
<div class="se-text-[10px] se-text-text-tertiary se-mt-1">WHY: ${item.rationale}</div>
|
|
253
|
+
`
|
|
254
|
+
: nothing}
|
|
255
|
+
</div>
|
|
256
|
+
`)}
|
|
257
|
+
`;
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
createRenderRoot() {
|
|
261
|
+
return this;
|
|
262
|
+
}
|
|
263
|
+
connectedCallback() {
|
|
264
|
+
super.connectedCallback();
|
|
265
|
+
window.addEventListener('popstate', this._onPopstate);
|
|
266
|
+
// Run detection on an interval (matches React's 2s interval)
|
|
267
|
+
this._detectionInterval = setInterval(() => this._runDetection(), 2000);
|
|
268
|
+
}
|
|
269
|
+
disconnectedCallback() {
|
|
270
|
+
window.removeEventListener('popstate', this._onPopstate);
|
|
271
|
+
if (this._detectionInterval) {
|
|
272
|
+
clearInterval(this._detectionInterval);
|
|
273
|
+
this._detectionInterval = null;
|
|
274
|
+
}
|
|
275
|
+
super.disconnectedCallback();
|
|
276
|
+
}
|
|
277
|
+
firstUpdated() {
|
|
278
|
+
this._runDetection();
|
|
279
|
+
// No auto-dirty on load — only dirty when user makes changes (matching React behavior).
|
|
280
|
+
}
|
|
281
|
+
render() {
|
|
282
|
+
if (!this.config) {
|
|
283
|
+
return html `<div class="se-p-8 se-text-center se-text-text-secondary se-text-sm">Loading...</div>`;
|
|
284
|
+
}
|
|
285
|
+
const items = flattenItems(this.config);
|
|
286
|
+
const editItem = this._editingKey !== null ? items.find((it) => it.key === this._editingKey) : null;
|
|
287
|
+
return html `
|
|
288
|
+
<div class="se-flex se-flex-col se-h-full">
|
|
289
|
+
<!-- Header (no Back button — panel provides that) -->
|
|
290
|
+
<div class="se-px-4 se-pt-3 se-pb-1">
|
|
291
|
+
<h2 class="se-m-0 se-text-base se-font-semibold se-text-text-primary">Navigation Tips</h2>
|
|
292
|
+
<p class="se-m-0 se-mt-0.5 se-text-xs se-text-text-secondary">
|
|
293
|
+
${items.length} tip${items.length !== 1 ? 's' : ''}
|
|
294
|
+
</p>
|
|
295
|
+
</div>
|
|
296
|
+
|
|
297
|
+
<div class="se-flex-1 se-overflow-auto se-p-4">
|
|
298
|
+
${editItem
|
|
299
|
+
? html `
|
|
300
|
+
<button type="button" @click=${() => {
|
|
301
|
+
this._editingKey = null;
|
|
302
|
+
}}
|
|
303
|
+
class="se-mb-3 se-py-1 se-px-2 se-rounded se-border-none se-bg-card-bg se-text-text-secondary se-text-xs se-cursor-pointer"
|
|
304
|
+
>← Back to list</button>
|
|
305
|
+
${this._renderEditMode(editItem)}
|
|
306
|
+
`
|
|
307
|
+
: this._renderListMode(items)}
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
`;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
NavEditorLit.properties = {
|
|
314
|
+
config: { attribute: false },
|
|
315
|
+
onChange: { attribute: false },
|
|
316
|
+
_editingKey: { state: true },
|
|
317
|
+
_detectionMap: { state: true },
|
|
318
|
+
};
|
|
319
|
+
customElements.define('se-nav-editor', NavEditorLit);
|
package/dist/editor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAmBH,OAAO,EACL,KAAK,gBAAgB,EAKtB,MAAM,SAAS,CAAC;AAmLjB,wBAAgB,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,gBAAgB,2CAkZvE;AAED;;GAEG;AACH,eAAO,MAAM,WAAW;;;;CAIvB,CAAC;AAEF,eAAO,MAAM,MAAM;;;;;;;CAGlB,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
package/dist/editor.js
CHANGED
|
@@ -6,7 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
6
6
|
* Displays a scannable list of tip cards with trigger, rationale,
|
|
7
7
|
* and inline editing. Includes detection badges and hover-to-highlight.
|
|
8
8
|
*/
|
|
9
|
-
import { DetectionBadge, DismissedSection, EditorBody, EditorCard,
|
|
9
|
+
import { DetectionBadge, DismissedSection, EditorBody, EditorCard, EditorHeader, EditorInput, EditorLayout, EmptyState, GroupHeader, TriggerJourney, useTriggerWhenStatus, } from '@syntrologie/shared-editor-ui';
|
|
10
10
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
11
11
|
import { describeTrigger, summarizeNavItem } from './summarize';
|
|
12
12
|
import { isOwnAction, } from './types';
|
|
@@ -240,7 +240,7 @@ export function NavEditor({ config, onChange, editor }) {
|
|
|
240
240
|
onChange(updated);
|
|
241
241
|
editor.setDirty(true);
|
|
242
242
|
}, [typedConfig, onChange, editor]);
|
|
243
|
-
const
|
|
243
|
+
const _handlePublish = useCallback(() => {
|
|
244
244
|
if (dismissedKeys.size > 0) {
|
|
245
245
|
const filtered = filterConfig(typedConfig, dismissedKeys);
|
|
246
246
|
onChange(filtered);
|
|
@@ -309,7 +309,7 @@ export function NavEditor({ config, onChange, editor }) {
|
|
|
309
309
|
})] })), dismissedItems.length > 0 && (_jsx(DismissedSection, { count: dismissedItems.length, children: dismissedItems.map((item) => (_jsxs("div", { className: "se-flex se-items-center se-gap-2 se-py-1.5 se-px-2.5 se-rounded-md se-border se-border-white/[0.03] se-bg-transparent se-mb-0.5 se-cursor-pointer se-text-xs se-text-slate-grey-6 se-opacity-60", children: [_jsx("span", { className: "se-flex-1 se-overflow-hidden se-text-ellipsis se-whitespace-nowrap se-line-through", children: item.summary }), _jsx("button", { type: "button", className: "se-py-0.5 se-px-1.5 se-rounded se-border-none se-bg-transparent se-text-blue-5 se-text-[11px] se-cursor-pointer se-shrink-0 se-leading-none", onClick: (e) => {
|
|
310
310
|
e.stopPropagation();
|
|
311
311
|
handleRestore(item.key);
|
|
312
|
-
}, children: "Restore" })] }, item.key))) }))] })) })
|
|
312
|
+
}, children: "Restore" })] }, item.key))) }))] })) })] }));
|
|
313
313
|
}
|
|
314
314
|
/**
|
|
315
315
|
* Editor panel configuration for the app registry.
|