@openpen/module-api 1.1.1 → 1.3.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/CHANGELOG.md +16 -0
- package/dist/_chunks/AppButtonDropdown.vue_vue_type_style_index_0_lang-B9dHU-LE.js +246 -0
- package/dist/_chunks/AppButtonDropdown.vue_vue_type_style_index_0_lang-B9dHU-LE.js.map +1 -0
- package/dist/_chunks/inject-keys-CRXCpN-M.js +196 -0
- package/dist/_chunks/inject-keys-CRXCpN-M.js.map +1 -0
- package/dist/_chunks/module-context-registry-Cn1IWYu9.js +16 -0
- package/dist/_chunks/module-context-registry-Cn1IWYu9.js.map +1 -0
- package/dist/_chunks/passthrough-DKU7JYuQ.js +6 -0
- package/dist/_chunks/passthrough-DKU7JYuQ.js.map +1 -0
- package/dist/cursors.d.ts +83 -0
- package/dist/define-module.d.ts +17 -0
- package/dist/host/color-utils.d.ts +11 -0
- package/{src/host/event-bus.ts → dist/host/event-bus.d.ts} +7 -5
- package/dist/host/host-commands.d.ts +1 -0
- package/dist/host/index.d.ts +24 -0
- package/dist/host/index.js +51 -0
- package/dist/host/index.js.map +1 -0
- package/{src/host/module-context-registry.ts → dist/host/module-context-registry.d.ts} +5 -19
- package/dist/host/passthrough.d.ts +2 -0
- package/dist/host/popup-anchor.d.ts +3 -0
- package/dist/host/registry.d.ts +39 -0
- package/dist/host/registry.js +37 -0
- package/dist/host/registry.js.map +1 -0
- package/dist/host/slot-entries.d.ts +2 -0
- package/dist/host/stroke-store.d.ts +6 -0
- package/dist/host/stroke-style.d.ts +3 -0
- package/dist/host/types.d.ts +176 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +349 -0
- package/dist/index.js.map +1 -0
- package/{src/inject-keys.ts → dist/inject-keys.d.ts} +37 -66
- package/{src/locale.ts → dist/locale.d.ts} +3 -22
- package/dist/slots.d.ts +57 -0
- package/dist/types/contributions.d.ts +288 -0
- package/dist/types/control-bar-layout.d.ts +227 -0
- package/dist/types/module.d.ts +208 -0
- package/{src/types/notification.ts → dist/types/notification.d.ts} +12 -14
- package/dist/types/tool.d.ts +77 -0
- package/dist/uikit/components/AppBanner.vue.d.ts +34 -0
- package/dist/uikit/components/AppButton.vue.d.ts +30 -0
- package/dist/uikit/components/AppButtonDropdown.vue.d.ts +52 -0
- package/dist/uikit/components/AppDialog.vue.d.ts +53 -0
- package/dist/uikit/components/AppPopover.vue.d.ts +43 -0
- package/dist/uikit/components/AppSegmented.vue.d.ts +19 -0
- package/dist/uikit/components/AppSelect.vue.d.ts +26 -0
- package/dist/uikit/components/AppSlider.vue.d.ts +43 -0
- package/dist/uikit/components/AppTabs.vue.d.ts +35 -0
- package/dist/uikit/components/AppToggle.vue.d.ts +15 -0
- package/dist/uikit/components/AppTooltip.vue.d.ts +27 -0
- package/dist/uikit/components/DialogHost.vue.d.ts +3 -0
- package/{src/uikit/composables/dialog-internal-keys.ts → dist/uikit/composables/dialog-internal-keys.d.ts} +3 -5
- package/dist/uikit/composables/useDialog.d.ts +399 -0
- package/dist/uikit/composables/useDialogPluginComponent.d.ts +9 -0
- package/dist/uikit/index.d.ts +42 -0
- package/dist/uikit/index.js +898 -0
- package/dist/uikit/index.js.map +1 -0
- package/{src/uikit/internal.ts → dist/uikit/internal.d.ts} +1 -1
- package/dist/uikit/internal.js +157 -0
- package/dist/uikit/internal.js.map +1 -0
- package/dist/uikit/module-api.css +851 -0
- package/dist/uikit/primitives.d.ts +42 -0
- package/{src/use-module-context.ts → dist/use-module-context.d.ts} +2 -15
- package/{src/validation.ts → dist/validation.d.ts} +2 -6
- package/package.json +28 -14
- package/src/cursors.ts +0 -369
- package/src/define-module.ts +0 -55
- package/src/host/color-utils.ts +0 -18
- package/src/host/host-commands.ts +0 -9
- package/src/host/index.ts +0 -53
- package/src/host/passthrough.ts +0 -10
- package/src/host/popup-anchor.ts +0 -14
- package/src/host/slot-entries.ts +0 -9
- package/src/host/stroke-store.ts +0 -20
- package/src/host/stroke-style.ts +0 -15
- package/src/index.ts +0 -131
- package/src/slots.ts +0 -135
- package/src/types/contributions.ts +0 -333
- package/src/types/control-bar-layout.ts +0 -174
- package/src/types/module.ts +0 -225
- package/src/types/tool.ts +0 -82
- package/src/uikit/components/.gitkeep +0 -0
- package/src/uikit/components/AppBanner.vue +0 -131
- package/src/uikit/components/AppButton.vue +0 -135
- package/src/uikit/components/AppDialog.vue +0 -393
- package/src/uikit/components/AppPopover.vue +0 -268
- package/src/uikit/components/AppSegmented.vue +0 -128
- package/src/uikit/components/AppSelect.vue +0 -235
- package/src/uikit/components/AppSlider.vue +0 -204
- package/src/uikit/components/AppTabs.vue +0 -163
- package/src/uikit/components/AppToggle.vue +0 -96
- package/src/uikit/components/AppTooltip.vue +0 -118
- package/src/uikit/components/DialogHost.vue +0 -272
- package/src/uikit/composables/useDialog.ts +0 -141
- package/src/uikit/composables/useDialogPluginComponent.ts +0 -27
- package/src/uikit/index.ts +0 -79
- package/src/uikit/primitives.ts +0 -150
- /package/{src → dist}/uikit/tokens.css +0 -0
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* DialogHost — private host-internal component.
|
|
4
|
-
*
|
|
5
|
-
* HOST-INTERNAL: Do not export from any public barrel. Plugin authors never
|
|
6
|
-
* interact with this component directly. The host mounts exactly one instance
|
|
7
|
-
* at bootstrap; it renders the currently active imperative dialog request from
|
|
8
|
-
* the useDialog() store.
|
|
9
|
-
*
|
|
10
|
-
* Rendering strategy:
|
|
11
|
-
* - Reads the active request from _activeRequest (module-scoped reactive ref).
|
|
12
|
-
* - Renders an <AppDialog> wired to that request's options.
|
|
13
|
-
* - On ok / cancel / dismiss: resolves the request's Promise and advances queue.
|
|
14
|
-
*
|
|
15
|
-
* Settings window constraint: AppDialog internally injects MODAL_MANAGER_KEY
|
|
16
|
-
* and CONTROL_BAR_ANIMATING_KEY. In the settings window those keys are not
|
|
17
|
-
* provided (ControlBar only exists in the overlay/main window), so the dialog
|
|
18
|
-
* falls back gracefully (no mutual exclusion, no animating-guard) — it still
|
|
19
|
-
* renders and resolves correctly, just without those host integrations.
|
|
20
|
-
*/
|
|
21
|
-
import { computed, inject, nextTick, provide, ref, watch } from 'vue'
|
|
22
|
-
import AppDialog from './AppDialog.vue'
|
|
23
|
-
// _activeRequest MUST come from the package barrel — see AppPopover.vue header
|
|
24
|
-
// comment. The runtime build externalises '@openpen/module-api/uikit' so host
|
|
25
|
-
// + uikit share the same module instance. A relative '../composables/useDialog'
|
|
26
|
-
// import bundles a duplicate ref into the host bundle, causing useDialog()
|
|
27
|
-
// callers to write to a different ref than DialogHost watches (dialog never
|
|
28
|
-
// opens in production).
|
|
29
|
-
import { _activeRequest, _resolveActive } from '@openpen/module-api/uikit'
|
|
30
|
-
import { HOST_DIALOG_OPEN_COUNT_KEY } from '@openpen/module-api'
|
|
31
|
-
import { DIALOG_PLUGIN_HANDLE_KEY } from '../composables/dialog-internal-keys'
|
|
32
|
-
import type { DialogPluginComponentHandle } from '../composables/useDialogPluginComponent'
|
|
33
|
-
|
|
34
|
-
const MODAL_ID = '__openpen_imperative_dialog__'
|
|
35
|
-
|
|
36
|
-
// Increment/decrement the host-level dialog-open counter so ControlBar can
|
|
37
|
-
// pause the auto-collapse timer while any imperative dialog is visible.
|
|
38
|
-
// Falls back to a local ref when not provided (settings window has no host bar).
|
|
39
|
-
const dialogOpenCount = inject(HOST_DIALOG_OPEN_COUNT_KEY, ref(0))
|
|
40
|
-
|
|
41
|
-
// Whether the dialog is open: true when there is an active request.
|
|
42
|
-
const isOpen = ref(false)
|
|
43
|
-
|
|
44
|
-
// Prompt value — bound to the input in prompt dialogs.
|
|
45
|
-
const promptValue = ref('')
|
|
46
|
-
|
|
47
|
-
// Track the prompt input element so we can focus it on open.
|
|
48
|
-
const promptInputRef = ref<HTMLInputElement | null>(null)
|
|
49
|
-
|
|
50
|
-
// Watch the active request: open when a request arrives, close when cleared.
|
|
51
|
-
watch(_activeRequest, (req) => {
|
|
52
|
-
if (req) {
|
|
53
|
-
if (req.kind === 'prompt') {
|
|
54
|
-
promptValue.value = req.opts.defaultValue ?? ''
|
|
55
|
-
}
|
|
56
|
-
if (!isOpen.value) {
|
|
57
|
-
dialogOpenCount.value++
|
|
58
|
-
}
|
|
59
|
-
isOpen.value = true
|
|
60
|
-
if (req.kind === 'prompt') {
|
|
61
|
-
nextTick(() => {
|
|
62
|
-
promptInputRef.value?.focus()
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
} else {
|
|
66
|
-
if (isOpen.value) {
|
|
67
|
-
dialogOpenCount.value = Math.max(0, dialogOpenCount.value - 1)
|
|
68
|
-
}
|
|
69
|
-
isOpen.value = false
|
|
70
|
-
}
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
// Derived from the active request for convenience.
|
|
74
|
-
const req = computed(() => _activeRequest.value)
|
|
75
|
-
const opts = computed(() => req.value?.opts)
|
|
76
|
-
|
|
77
|
-
function handleOk() {
|
|
78
|
-
const r = _activeRequest.value
|
|
79
|
-
if (!r) return
|
|
80
|
-
if (r.kind === 'confirm') {
|
|
81
|
-
r.resolve(true)
|
|
82
|
-
} else if (r.kind === 'alert') {
|
|
83
|
-
r.resolve()
|
|
84
|
-
} else if (r.kind === 'prompt') {
|
|
85
|
-
r.resolve(promptValue.value)
|
|
86
|
-
}
|
|
87
|
-
// custom: custom component handles its own resolution via ok/cancel/dismiss
|
|
88
|
-
_resolveActive()
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function handleCancel() {
|
|
92
|
-
const r = _activeRequest.value
|
|
93
|
-
if (!r) return
|
|
94
|
-
if (r.kind === 'confirm') {
|
|
95
|
-
r.resolve(false)
|
|
96
|
-
} else if (r.kind === 'prompt') {
|
|
97
|
-
r.resolve(null)
|
|
98
|
-
}
|
|
99
|
-
_resolveActive()
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function handleUpdateOpen(v: boolean) {
|
|
103
|
-
if (!v) {
|
|
104
|
-
// Dialog closed via ESC / close button (not persistent).
|
|
105
|
-
const r = _activeRequest.value
|
|
106
|
-
if (!r) return
|
|
107
|
-
if (r.kind === 'confirm') {
|
|
108
|
-
r.resolve(false)
|
|
109
|
-
} else if (r.kind === 'alert') {
|
|
110
|
-
r.resolve()
|
|
111
|
-
} else if (r.kind === 'prompt') {
|
|
112
|
-
r.resolve(null)
|
|
113
|
-
} else if (r.kind === 'custom') {
|
|
114
|
-
r.resolve(null)
|
|
115
|
-
}
|
|
116
|
-
_resolveActive()
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// ── Custom component plugin handle ───────────────────────────────────────────
|
|
121
|
-
//
|
|
122
|
-
// Provide the handle so custom components can call ok/cancel/dismiss via
|
|
123
|
-
// useDialogPluginComponent(). We provide at this component level so only the
|
|
124
|
-
// custom component subtree gets it.
|
|
125
|
-
|
|
126
|
-
function customOk(payload?: unknown) {
|
|
127
|
-
const r = _activeRequest.value
|
|
128
|
-
if (!r || r.kind !== 'custom') return
|
|
129
|
-
r.resolve(payload ?? null)
|
|
130
|
-
_resolveActive()
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function customCancel() {
|
|
134
|
-
const r = _activeRequest.value
|
|
135
|
-
if (!r || r.kind !== 'custom') return
|
|
136
|
-
r.resolve(null)
|
|
137
|
-
_resolveActive()
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const pluginHandle: DialogPluginComponentHandle<unknown> = {
|
|
141
|
-
ok: customOk,
|
|
142
|
-
cancel: customCancel,
|
|
143
|
-
dismiss: customCancel,
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
provide(DIALOG_PLUGIN_HANDLE_KEY, pluginHandle)
|
|
147
|
-
</script>
|
|
148
|
-
|
|
149
|
-
<template>
|
|
150
|
-
<!-- DialogHost renders nothing when there is no active request. -->
|
|
151
|
-
<AppDialog
|
|
152
|
-
v-if="req"
|
|
153
|
-
:modal-id="MODAL_ID"
|
|
154
|
-
:title="opts!.title"
|
|
155
|
-
:open="isOpen"
|
|
156
|
-
:persistent="opts!.persistent"
|
|
157
|
-
:danger="opts!.danger"
|
|
158
|
-
@update:open="handleUpdateOpen"
|
|
159
|
-
>
|
|
160
|
-
<!-- Body slot: varies by kind -->
|
|
161
|
-
<template v-if="req.kind === 'confirm' || req.kind === 'alert'">
|
|
162
|
-
<p class="openpen-dialog-host-message">{{ (opts as { message: string }).message }}</p>
|
|
163
|
-
</template>
|
|
164
|
-
|
|
165
|
-
<template v-else-if="req.kind === 'prompt'">
|
|
166
|
-
<p class="openpen-dialog-host-message">{{ (opts as { message: string }).message }}</p>
|
|
167
|
-
<input
|
|
168
|
-
ref="promptInputRef"
|
|
169
|
-
v-model="promptValue"
|
|
170
|
-
class="openpen-dialog-host-input"
|
|
171
|
-
:placeholder="(opts as { placeholder?: string }).placeholder"
|
|
172
|
-
type="text"
|
|
173
|
-
@keydown.enter="handleOk"
|
|
174
|
-
@keydown.esc="handleCancel"
|
|
175
|
-
/>
|
|
176
|
-
</template>
|
|
177
|
-
|
|
178
|
-
<template v-else-if="req.kind === 'custom'">
|
|
179
|
-
<!-- Render the custom component; it gets ok/cancel/dismiss via injection. -->
|
|
180
|
-
<component
|
|
181
|
-
:is="(opts as { component: unknown }).component"
|
|
182
|
-
v-bind="(opts as { componentProps?: Record<string, unknown> }).componentProps ?? {}"
|
|
183
|
-
/>
|
|
184
|
-
</template>
|
|
185
|
-
|
|
186
|
-
<!-- Footer slot: only for confirm/alert/prompt; custom owns its own buttons. -->
|
|
187
|
-
<template v-if="req.kind !== 'custom'" #footer>
|
|
188
|
-
<button
|
|
189
|
-
v-if="req.kind === 'confirm' || req.kind === 'prompt'"
|
|
190
|
-
class="openpen-btn openpen-btn-secondary"
|
|
191
|
-
@click="handleCancel"
|
|
192
|
-
>
|
|
193
|
-
{{ (opts as { cancelLabel?: string }).cancelLabel ?? 'Cancel' }}
|
|
194
|
-
</button>
|
|
195
|
-
<button
|
|
196
|
-
class="openpen-btn openpen-btn-primary"
|
|
197
|
-
:class="{ 'openpen-btn-danger': opts!.danger }"
|
|
198
|
-
@click="handleOk"
|
|
199
|
-
>
|
|
200
|
-
{{ (opts as { okLabel?: string }).okLabel ?? 'OK' }}
|
|
201
|
-
</button>
|
|
202
|
-
</template>
|
|
203
|
-
</AppDialog>
|
|
204
|
-
</template>
|
|
205
|
-
|
|
206
|
-
<style>
|
|
207
|
-
/* ── DialogHost utility styles ───────────────────────────────────────────── */
|
|
208
|
-
.openpen-dialog-host-message {
|
|
209
|
-
margin: 0 0 var(--openpen-space-md, 12px) 0;
|
|
210
|
-
color: var(--openpen-color-text-primary, #fff);
|
|
211
|
-
line-height: 1.5;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
.openpen-dialog-host-input {
|
|
215
|
-
display: block;
|
|
216
|
-
width: 100%;
|
|
217
|
-
box-sizing: border-box;
|
|
218
|
-
padding: 6px 10px;
|
|
219
|
-
border: 1px solid var(--openpen-color-border-hi, rgba(255,255,255,0.2));
|
|
220
|
-
border-radius: var(--openpen-radius-sm, 6px);
|
|
221
|
-
background: var(--openpen-color-control, rgba(255,255,255,0.08));
|
|
222
|
-
color: var(--openpen-color-text-primary, #fff);
|
|
223
|
-
font-size: 13px;
|
|
224
|
-
outline: none;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
.openpen-dialog-host-input:focus {
|
|
228
|
-
border-color: var(--accent, #7c7cff);
|
|
229
|
-
box-shadow: 0 0 0 2px var(--accent-bg, rgba(124,124,255,0.18));
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/* ── DialogHost button styles ─────────────────────────────────────────────── */
|
|
233
|
-
.openpen-btn {
|
|
234
|
-
display: inline-flex;
|
|
235
|
-
align-items: center;
|
|
236
|
-
justify-content: center;
|
|
237
|
-
padding: 6px 16px;
|
|
238
|
-
border-radius: var(--openpen-radius-sm, 6px);
|
|
239
|
-
font-size: 13px;
|
|
240
|
-
font-weight: 500;
|
|
241
|
-
cursor: pointer;
|
|
242
|
-
border: none;
|
|
243
|
-
transition: background var(--openpen-duration-fast, 120ms), color var(--openpen-duration-fast, 120ms);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
.openpen-btn-secondary {
|
|
247
|
-
background: var(--openpen-color-control, rgba(255,255,255,0.08));
|
|
248
|
-
color: var(--openpen-color-text-primary, #fff);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
.openpen-btn-secondary:hover {
|
|
252
|
-
background: var(--openpen-color-control-hover, rgba(255,255,255,0.14));
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
.openpen-btn-primary {
|
|
256
|
-
background: var(--accent, #7c7cff);
|
|
257
|
-
color: #fff;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
.openpen-btn-primary:hover {
|
|
261
|
-
opacity: 0.9;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
.openpen-btn-danger {
|
|
265
|
-
background: var(--openpen-color-danger, #e05252);
|
|
266
|
-
color: #fff;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
.openpen-btn-danger:hover {
|
|
270
|
-
opacity: 0.9;
|
|
271
|
-
}
|
|
272
|
-
</style>
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useDialog — imperative dialog API for OpenPen modules.
|
|
3
|
-
*
|
|
4
|
-
* Returns a DialogApi with confirm/alert/prompt/custom methods that each
|
|
5
|
-
* return a Promise. At most one imperative dialog is open at a time; extra
|
|
6
|
-
* calls are queued and executed sequentially.
|
|
7
|
-
*
|
|
8
|
-
* The active request is stored in a module-scoped reactive store. DialogHost
|
|
9
|
-
* (mounted by the host at bootstrap) reads this store and renders the dialog.
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* const dialog = useDialog()
|
|
13
|
-
* const ok = await dialog.confirm({ title: 'Delete?', message: '...' })
|
|
14
|
-
*/
|
|
15
|
-
import type { Component } from 'vue'
|
|
16
|
-
import { ref } from 'vue'
|
|
17
|
-
|
|
18
|
-
// ── Public option types ───────────────────────────────────────────────────────
|
|
19
|
-
|
|
20
|
-
interface BaseDialogOptions {
|
|
21
|
-
title: string
|
|
22
|
-
persistent?: boolean
|
|
23
|
-
danger?: boolean
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface DialogConfirmOptions extends BaseDialogOptions {
|
|
27
|
-
message: string
|
|
28
|
-
okLabel?: string
|
|
29
|
-
cancelLabel?: string
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface DialogAlertOptions extends BaseDialogOptions {
|
|
33
|
-
message: string
|
|
34
|
-
okLabel?: string
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface DialogPromptOptions extends BaseDialogOptions {
|
|
38
|
-
message: string
|
|
39
|
-
defaultValue?: string
|
|
40
|
-
placeholder?: string
|
|
41
|
-
okLabel?: string
|
|
42
|
-
cancelLabel?: string
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface DialogCustomOptions<T = unknown> extends BaseDialogOptions {
|
|
46
|
-
component: Component
|
|
47
|
-
componentProps?: Record<string, unknown>
|
|
48
|
-
/** Phantom type field for TypeScript to infer the resolution payload type. */
|
|
49
|
-
__payloadType?: T
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// ── Internal request types (tagged union) ─────────────────────────────────────
|
|
53
|
-
|
|
54
|
-
type DialogRequest =
|
|
55
|
-
| { kind: 'confirm'; opts: DialogConfirmOptions; resolve: (v: boolean) => void }
|
|
56
|
-
| { kind: 'alert'; opts: DialogAlertOptions; resolve: () => void }
|
|
57
|
-
| { kind: 'prompt'; opts: DialogPromptOptions; resolve: (v: string | null) => void }
|
|
58
|
-
| { kind: 'custom'; opts: DialogCustomOptions<unknown>; resolve: (v: unknown) => void }
|
|
59
|
-
|
|
60
|
-
// ── Module-scoped store ───────────────────────────────────────────────────────
|
|
61
|
-
//
|
|
62
|
-
// Single reactive ref holds the currently rendered request. DialogHost watches
|
|
63
|
-
// this and renders the appropriate dialog. When DialogHost resolves a request,
|
|
64
|
-
// it calls resolveActive() which drains the queue.
|
|
65
|
-
|
|
66
|
-
export const _activeRequest = ref<DialogRequest | null>(null)
|
|
67
|
-
|
|
68
|
-
const _queue: (() => void)[] = []
|
|
69
|
-
let _processing = false
|
|
70
|
-
|
|
71
|
-
function _enqueue(run: () => void): void {
|
|
72
|
-
if (_processing) {
|
|
73
|
-
_queue.push(run)
|
|
74
|
-
} else {
|
|
75
|
-
_processing = true
|
|
76
|
-
run()
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Called by DialogHost when the dialog is resolved (ok/cancel/dismiss).
|
|
82
|
-
* Advances the queue if there are pending requests.
|
|
83
|
-
*/
|
|
84
|
-
export function _resolveActive(): void {
|
|
85
|
-
_activeRequest.value = null
|
|
86
|
-
const next = _queue.shift()
|
|
87
|
-
if (next) {
|
|
88
|
-
next()
|
|
89
|
-
} else {
|
|
90
|
-
_processing = false
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ── Public API ────────────────────────────────────────────────────────────────
|
|
95
|
-
|
|
96
|
-
export interface DialogApi {
|
|
97
|
-
confirm(opts: DialogConfirmOptions): Promise<boolean>
|
|
98
|
-
alert(opts: DialogAlertOptions): Promise<void>
|
|
99
|
-
prompt(opts: DialogPromptOptions): Promise<string | null>
|
|
100
|
-
custom<T = unknown>(opts: DialogCustomOptions<T>): Promise<T | null>
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function useDialog(): DialogApi {
|
|
104
|
-
return {
|
|
105
|
-
confirm(opts: DialogConfirmOptions): Promise<boolean> {
|
|
106
|
-
return new Promise<boolean>((resolve) => {
|
|
107
|
-
_enqueue(() => {
|
|
108
|
-
_activeRequest.value = { kind: 'confirm', opts, resolve }
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
},
|
|
112
|
-
|
|
113
|
-
alert(opts: DialogAlertOptions): Promise<void> {
|
|
114
|
-
return new Promise<void>((resolve) => {
|
|
115
|
-
_enqueue(() => {
|
|
116
|
-
_activeRequest.value = { kind: 'alert', opts, resolve }
|
|
117
|
-
})
|
|
118
|
-
})
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
prompt(opts: DialogPromptOptions): Promise<string | null> {
|
|
122
|
-
return new Promise<string | null>((resolve) => {
|
|
123
|
-
_enqueue(() => {
|
|
124
|
-
_activeRequest.value = { kind: 'prompt', opts, resolve }
|
|
125
|
-
})
|
|
126
|
-
})
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
custom<T = unknown>(opts: DialogCustomOptions<T>): Promise<T | null> {
|
|
130
|
-
return new Promise<T | null>((resolve) => {
|
|
131
|
-
_enqueue(() => {
|
|
132
|
-
_activeRequest.value = {
|
|
133
|
-
kind: 'custom',
|
|
134
|
-
opts: opts as DialogCustomOptions<unknown>,
|
|
135
|
-
resolve: resolve as (v: unknown) => void,
|
|
136
|
-
}
|
|
137
|
-
})
|
|
138
|
-
})
|
|
139
|
-
},
|
|
140
|
-
}
|
|
141
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useDialogPluginComponent — composable for components rendered via useDialog().custom().
|
|
3
|
-
*
|
|
4
|
-
* The custom component calls this to get ok/cancel/dismiss callbacks, which
|
|
5
|
-
* resolve the Promise returned by useDialog().custom().
|
|
6
|
-
*/
|
|
7
|
-
import { inject } from 'vue'
|
|
8
|
-
import { DIALOG_PLUGIN_HANDLE_KEY } from './dialog-internal-keys'
|
|
9
|
-
|
|
10
|
-
export interface DialogPluginComponentHandle<T = unknown> {
|
|
11
|
-
/** Resolve with a payload value and close the dialog. */
|
|
12
|
-
ok(payload?: T): void
|
|
13
|
-
/** Resolve with null and close the dialog. */
|
|
14
|
-
cancel(): void
|
|
15
|
-
/** Alias for cancel() — resolves with null. */
|
|
16
|
-
dismiss(): void
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function useDialogPluginComponent<T = unknown>(): DialogPluginComponentHandle<T> {
|
|
20
|
-
const handle = inject(DIALOG_PLUGIN_HANDLE_KEY)
|
|
21
|
-
if (!handle) {
|
|
22
|
-
throw new Error(
|
|
23
|
-
'useDialogPluginComponent() must be called inside a component rendered via useDialog().custom()'
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
return handle as DialogPluginComponentHandle<T>
|
|
27
|
-
}
|
package/src/uikit/index.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @openpen/module-api/uikit — Three-layer API surface
|
|
3
|
-
*
|
|
4
|
-
* Layer 1 — High-level Wrappers (80% case)
|
|
5
|
-
* OpenPen-opinionated components with preset styles + auto-injected host
|
|
6
|
-
* context. Plugin authors should start here: no knowledge of inject keys
|
|
7
|
-
* or Reka UI primitives required.
|
|
8
|
-
*
|
|
9
|
-
* Layer 2 — Primitive Re-exports (20% advanced)
|
|
10
|
-
* Raw Reka UI headless components. Use when you need full control over
|
|
11
|
-
* markup / styling but still want a11y / focus / keyboard nav behaviour.
|
|
12
|
-
* Note: you must manually handle modal-manager, animating guard,
|
|
13
|
-
* passthrough, and teleport target when walking this layer.
|
|
14
|
-
*
|
|
15
|
-
* Layer 3 — Escape Hatch (5% fully custom)
|
|
16
|
-
* Plugin installs reka-ui (or any library) directly in its own package.json.
|
|
17
|
-
* UIKit MUST NOT block this. Not handled here — plugin author's choice.
|
|
18
|
-
*
|
|
19
|
-
* See docs/reference/uikit.md for the full trade-off description.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
// ── Layer 1: High-level Wrappers ──────────────────────────────────────────────
|
|
23
|
-
|
|
24
|
-
export { default as AppPopover } from './components/AppPopover.vue'
|
|
25
|
-
export type { AppPopoverProps, Placement } from './components/AppPopover.vue'
|
|
26
|
-
|
|
27
|
-
export { default as AppSlider } from './components/AppSlider.vue'
|
|
28
|
-
export { default as AppToggle } from './components/AppToggle.vue'
|
|
29
|
-
export { default as AppSegmented } from './components/AppSegmented.vue'
|
|
30
|
-
|
|
31
|
-
export { default as AppDialog } from './components/AppDialog.vue'
|
|
32
|
-
export type { AppDialogProps } from './components/AppDialog.vue'
|
|
33
|
-
|
|
34
|
-
export { useDialog } from './composables/useDialog'
|
|
35
|
-
export type {
|
|
36
|
-
DialogConfirmOptions,
|
|
37
|
-
DialogAlertOptions,
|
|
38
|
-
DialogPromptOptions,
|
|
39
|
-
DialogCustomOptions,
|
|
40
|
-
} from './composables/useDialog'
|
|
41
|
-
export { useDialogPluginComponent } from './composables/useDialogPluginComponent'
|
|
42
|
-
|
|
43
|
-
// Host-internal: shared by DialogHost so it sees the same _activeRequest ref
|
|
44
|
-
// instance as useDialog() callers. MUST be exported via this barrel — the
|
|
45
|
-
// uikit runtime bundle (dist/openpen-runtime/module-api-uikit.js) is the
|
|
46
|
-
// single shared module instance referenced by the importmap; a relative
|
|
47
|
-
// '../composables/useDialog' import in DialogHost would bundle a second
|
|
48
|
-
// duplicate ref into the host bundle (see project_dev_prod_parity.md), so
|
|
49
|
-
// dialog.confirm() would never reach DialogHost in production.
|
|
50
|
-
// Underscore-prefixed: NOT part of the plugin-facing API surface.
|
|
51
|
-
export { _activeRequest, _resolveActive } from './composables/useDialog'
|
|
52
|
-
export { default as AppSelect } from './components/AppSelect.vue'
|
|
53
|
-
export type { AppSelectProps, SelectOption } from './components/AppSelect.vue'
|
|
54
|
-
export { default as AppTooltip } from './components/AppTooltip.vue'
|
|
55
|
-
export type { AppTooltipProps, TooltipSide } from './components/AppTooltip.vue'
|
|
56
|
-
export { default as AppTabs } from './components/AppTabs.vue'
|
|
57
|
-
export type { AppTabsProps, AppTabItem } from './components/AppTabs.vue'
|
|
58
|
-
|
|
59
|
-
export { default as AppBanner } from './components/AppBanner.vue'
|
|
60
|
-
export type { BannerVariant } from './components/AppBanner.vue'
|
|
61
|
-
|
|
62
|
-
export { default as AppButton } from './components/AppButton.vue'
|
|
63
|
-
|
|
64
|
-
// ── Layer 2: Primitive Re-exports ────────────────────────────────────────────
|
|
65
|
-
//
|
|
66
|
-
// Re-export entire primitives namespace.
|
|
67
|
-
// MUST NOT re-export any reka-ui type through this index — wrapper types only.
|
|
68
|
-
// (primitives.ts is the designated channel for the raw headless exports.)
|
|
69
|
-
export * from './primitives'
|
|
70
|
-
|
|
71
|
-
// ── Host context inject keys ──────────────────────────────────────────────────
|
|
72
|
-
//
|
|
73
|
-
// Keys are defined in @openpen/module-api root inject-keys.ts and re-exported
|
|
74
|
-
// here for convenience. Plugin authors who need to walk Layer 2 should import
|
|
75
|
-
// from the root package:
|
|
76
|
-
// import { WRAPPER_EL_KEY, MODAL_MANAGER_KEY } from '@openpen/module-api'
|
|
77
|
-
//
|
|
78
|
-
// Root index.ts already covers:
|
|
79
|
-
// WRAPPER_EL_KEY, SNAP_EDGE_KEY, MODAL_MANAGER_KEY, ANCHOR_EL_KEY.
|
package/src/uikit/primitives.ts
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reka UI primitive re-exports.
|
|
3
|
-
*
|
|
4
|
-
* This module exposes the raw Reka UI headless components for plugin authors
|
|
5
|
-
* who need full control over markup and styling but still want Reka UI's
|
|
6
|
-
* a11y / focus / keyboard navigation behaviour.
|
|
7
|
-
*
|
|
8
|
-
* Usage (escape hatch from high-level wrappers):
|
|
9
|
-
* import { PopoverRoot, PopoverTrigger, ... } from '@openpen/module-api/uikit'
|
|
10
|
-
* // or directly:
|
|
11
|
-
* import { ... } from '@openpen/module-api/uikit/primitives' (if re-exported)
|
|
12
|
-
*
|
|
13
|
-
* WARNING: Walking this layer means you must manually handle:
|
|
14
|
-
* - Modal manager mutual exclusion
|
|
15
|
-
* - ControlBar animation auto-close (inject CONTROL_BAR_ANIMATING_KEY)
|
|
16
|
-
* - Mouse passthrough guard (usePassthroughGuard)
|
|
17
|
-
* - Teleport target (inject WRAPPER_EL_KEY)
|
|
18
|
-
*
|
|
19
|
-
* See docs/reference/uikit.md for the full trade-off description.
|
|
20
|
-
*
|
|
21
|
-
* All v1.0 high-level wrappers are implemented — prefer them over
|
|
22
|
-
* the corresponding primitive:
|
|
23
|
-
* Popover → AppPopover
|
|
24
|
-
* Dialog → AppDialog
|
|
25
|
-
* Slider → AppSlider
|
|
26
|
-
* Switch → AppToggle
|
|
27
|
-
* RadioGroup → AppSegmented
|
|
28
|
-
* Select → AppSelect
|
|
29
|
-
* Tooltip → AppTooltip
|
|
30
|
-
* Tabs → AppTabs
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
// ── Popover ──────────────────────────────────────────────────────────────────
|
|
34
|
-
export {
|
|
35
|
-
PopoverRoot,
|
|
36
|
-
PopoverTrigger,
|
|
37
|
-
PopoverPortal,
|
|
38
|
-
PopoverContent,
|
|
39
|
-
PopoverArrow,
|
|
40
|
-
PopoverClose,
|
|
41
|
-
PopoverAnchor,
|
|
42
|
-
} from 'reka-ui'
|
|
43
|
-
|
|
44
|
-
// ── Dialog (Modal) ────────────────────────────────────────────────────────────
|
|
45
|
-
export {
|
|
46
|
-
DialogRoot,
|
|
47
|
-
DialogTrigger,
|
|
48
|
-
DialogPortal,
|
|
49
|
-
DialogContent,
|
|
50
|
-
DialogOverlay,
|
|
51
|
-
DialogClose,
|
|
52
|
-
DialogTitle,
|
|
53
|
-
DialogDescription,
|
|
54
|
-
} from 'reka-ui'
|
|
55
|
-
|
|
56
|
-
// ── Slider ────────────────────────────────────────────────────────────────────
|
|
57
|
-
export {
|
|
58
|
-
SliderRoot,
|
|
59
|
-
SliderTrack,
|
|
60
|
-
SliderRange,
|
|
61
|
-
SliderThumb,
|
|
62
|
-
} from 'reka-ui'
|
|
63
|
-
|
|
64
|
-
// ── Switch (Toggle) ───────────────────────────────────────────────────────────
|
|
65
|
-
export {
|
|
66
|
-
SwitchRoot,
|
|
67
|
-
SwitchThumb,
|
|
68
|
-
} from 'reka-ui'
|
|
69
|
-
|
|
70
|
-
// ── RadioGroup (Segmented) ────────────────────────────────────────────────────
|
|
71
|
-
export {
|
|
72
|
-
RadioGroupRoot,
|
|
73
|
-
RadioGroupItem,
|
|
74
|
-
RadioGroupIndicator,
|
|
75
|
-
} from 'reka-ui'
|
|
76
|
-
|
|
77
|
-
// ── Select ────────────────────────────────────────────────────────────────────
|
|
78
|
-
export {
|
|
79
|
-
SelectRoot,
|
|
80
|
-
SelectTrigger,
|
|
81
|
-
SelectPortal,
|
|
82
|
-
SelectContent,
|
|
83
|
-
SelectViewport,
|
|
84
|
-
SelectItem,
|
|
85
|
-
SelectItemText,
|
|
86
|
-
SelectItemIndicator,
|
|
87
|
-
SelectLabel,
|
|
88
|
-
SelectGroup,
|
|
89
|
-
SelectSeparator,
|
|
90
|
-
SelectScrollUpButton,
|
|
91
|
-
SelectScrollDownButton,
|
|
92
|
-
SelectArrow,
|
|
93
|
-
SelectValue,
|
|
94
|
-
SelectIcon,
|
|
95
|
-
} from 'reka-ui'
|
|
96
|
-
|
|
97
|
-
// ── Tooltip ───────────────────────────────────────────────────────────────────
|
|
98
|
-
export {
|
|
99
|
-
TooltipProvider,
|
|
100
|
-
TooltipRoot,
|
|
101
|
-
TooltipTrigger,
|
|
102
|
-
TooltipPortal,
|
|
103
|
-
TooltipContent,
|
|
104
|
-
TooltipArrow,
|
|
105
|
-
} from 'reka-ui'
|
|
106
|
-
|
|
107
|
-
// ── Tabs ──────────────────────────────────────────────────────────────────────
|
|
108
|
-
export {
|
|
109
|
-
TabsRoot,
|
|
110
|
-
TabsList,
|
|
111
|
-
TabsTrigger,
|
|
112
|
-
TabsContent,
|
|
113
|
-
} from 'reka-ui'
|
|
114
|
-
|
|
115
|
-
// ── NumberField (numeric spinner with +/- buttons) ────────────────────────────
|
|
116
|
-
export {
|
|
117
|
-
NumberFieldRoot,
|
|
118
|
-
NumberFieldInput,
|
|
119
|
-
NumberFieldIncrement,
|
|
120
|
-
NumberFieldDecrement,
|
|
121
|
-
} from 'reka-ui'
|
|
122
|
-
|
|
123
|
-
// ── TagsInput (chip / token input) ────────────────────────────────────────────
|
|
124
|
-
export {
|
|
125
|
-
TagsInputRoot,
|
|
126
|
-
TagsInputInput,
|
|
127
|
-
TagsInputItem,
|
|
128
|
-
TagsInputItemText,
|
|
129
|
-
TagsInputItemDelete,
|
|
130
|
-
TagsInputClear,
|
|
131
|
-
} from 'reka-ui'
|
|
132
|
-
|
|
133
|
-
// ── Combobox (searchable dropdown with free-text input) ───────────────────────
|
|
134
|
-
export {
|
|
135
|
-
ComboboxRoot,
|
|
136
|
-
ComboboxAnchor,
|
|
137
|
-
ComboboxInput,
|
|
138
|
-
ComboboxTrigger,
|
|
139
|
-
ComboboxPortal,
|
|
140
|
-
ComboboxContent,
|
|
141
|
-
ComboboxViewport,
|
|
142
|
-
ComboboxItem,
|
|
143
|
-
ComboboxItemIndicator,
|
|
144
|
-
ComboboxGroup,
|
|
145
|
-
ComboboxLabel,
|
|
146
|
-
ComboboxSeparator,
|
|
147
|
-
ComboboxEmpty,
|
|
148
|
-
ComboboxArrow,
|
|
149
|
-
ComboboxCancel,
|
|
150
|
-
} from 'reka-ui'
|
|
File without changes
|