@picked/svelte 0.0.63 → 0.0.64-next.2
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/plugins/devtools/DevToolsExperience.svelte +7 -0
- package/dist/plugins/devtools/components/AdaptedToolHost.svelte +103 -0
- package/dist/plugins/devtools/customTools.d.ts +26 -0
- package/dist/plugins/devtools/customTools.js +1 -0
- package/dist/plugins/devtools/index.d.ts +2 -0
- package/dist/plugins/devtools/plugin.d.ts +12 -0
- package/dist/plugins/devtools/plugin.js +1 -1
- package/dist/plugins/devtools/screens/custom-tools/CustomToolsScreen.svelte +29 -0
- package/dist/plugins/framework/plugin.d.ts +1 -2
- package/dist/plugins/framework/plugin.js +1 -1
- package/dist/render/browser.d.ts +14 -5
- package/dist/render/browser.js +1 -1
- package/package.json +2 -2
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import ContentListScreen from './screens/content/ContentListScreen.svelte'
|
|
24
24
|
import FeaturesListScreen from './screens/features/FeaturesListScreen.svelte'
|
|
25
25
|
import EventsListScreen from './screens/events/EventsListScreen.svelte'
|
|
26
|
+
import CustomToolsScreen from './screens/custom-tools/CustomToolsScreen.svelte'
|
|
26
27
|
import PickedIcon from './PickedIcon.svelte'
|
|
27
28
|
|
|
28
29
|
const experience = getExperience()
|
|
@@ -116,6 +117,12 @@
|
|
|
116
117
|
component: ResponsesListScreen,
|
|
117
118
|
props: {},
|
|
118
119
|
},
|
|
120
|
+
{
|
|
121
|
+
id: 'custom-tools',
|
|
122
|
+
label: 'Custom Tools',
|
|
123
|
+
component: CustomToolsScreen,
|
|
124
|
+
props: {},
|
|
125
|
+
},
|
|
119
126
|
]
|
|
120
127
|
|
|
121
128
|
const isOpen = experience.isOpenStore
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/** @file Renders a custom devtools tool by delegating to its framework's registered renderer. */
|
|
3
|
+
import { onDestroy } from 'svelte'
|
|
4
|
+
import type { Framework } from '@picked/client'
|
|
5
|
+
import { getExperience, getPicked } from '../../../hooks'
|
|
6
|
+
import type { CustomTool } from '../customTools'
|
|
7
|
+
import { Banner } from '../ui'
|
|
8
|
+
|
|
9
|
+
export let tool: CustomTool
|
|
10
|
+
|
|
11
|
+
const client = getPicked()
|
|
12
|
+
const experience = getExperience()
|
|
13
|
+
const frameworkStore = client.frameworks.getByName(tool.component.frameworkName)
|
|
14
|
+
|
|
15
|
+
let host: HTMLDivElement | undefined
|
|
16
|
+
let rendered = false
|
|
17
|
+
let renderFailed = false
|
|
18
|
+
let destroyed = false
|
|
19
|
+
let activeFramework: Framework | undefined
|
|
20
|
+
let activeShadow: ShadowRoot | undefined
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Records a tool render failure: surfaces a banner and logs to the console. A consumer tool's
|
|
24
|
+
* render failure is deliberately not routed through the client event bus, so a third-party tool
|
|
25
|
+
* bug never reaches the platform's Sentry exception stream.
|
|
26
|
+
*
|
|
27
|
+
* @param {unknown} error - The error thrown or rejected by the framework renderer.
|
|
28
|
+
* @example
|
|
29
|
+
* handleRenderError(new Error('tool blew up'))
|
|
30
|
+
*/
|
|
31
|
+
function handleRenderError(error: unknown) {
|
|
32
|
+
renderFailed = true
|
|
33
|
+
// eslint-disable-next-line no-console -- surface tool failures locally; consumer errors must not reach Sentry
|
|
34
|
+
console.error(`Custom devtools tool "${tool.label}" failed to render`, error)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Tears down the tool's mounted instance using the framework that rendered it. Idempotent: the
|
|
39
|
+
* renderer's unmount is a no-op once the instance is gone, so calling it twice is safe.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* teardown()
|
|
43
|
+
*/
|
|
44
|
+
function teardown() {
|
|
45
|
+
if (activeShadow && activeFramework?.unmount) {
|
|
46
|
+
activeFramework.unmount(activeShadow)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
$: framework = $frameworkStore
|
|
51
|
+
|
|
52
|
+
$: if (framework && host && !rendered) {
|
|
53
|
+
rendered = true
|
|
54
|
+
const shadow = host.shadowRoot ?? host.attachShadow({ mode: 'open' })
|
|
55
|
+
const fw = framework
|
|
56
|
+
activeFramework = fw
|
|
57
|
+
activeShadow = shadow
|
|
58
|
+
try {
|
|
59
|
+
void Promise.resolve(fw.render(shadow, tool.component, client, experience))
|
|
60
|
+
.then(() => {
|
|
61
|
+
if (destroyed) teardown()
|
|
62
|
+
})
|
|
63
|
+
.catch(handleRenderError)
|
|
64
|
+
} catch (error) {
|
|
65
|
+
handleRenderError(error)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
onDestroy(() => {
|
|
70
|
+
destroyed = true
|
|
71
|
+
teardown()
|
|
72
|
+
})
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
{#if framework}
|
|
76
|
+
<div class="adapted-tool-host" bind:this={host} hidden={renderFailed}></div>
|
|
77
|
+
{#if renderFailed}
|
|
78
|
+
<div class="adapted-tool-fallback">
|
|
79
|
+
<Banner status="error" title="Tool failed to render">
|
|
80
|
+
The "{tool.label}" tool threw while rendering. See the browser console for details.
|
|
81
|
+
</Banner>
|
|
82
|
+
</div>
|
|
83
|
+
{/if}
|
|
84
|
+
{:else}
|
|
85
|
+
<div class="adapted-tool-fallback">
|
|
86
|
+
<Banner status="warning" title="Framework not registered">
|
|
87
|
+
This tool renders a "{tool.component.frameworkName}" component, but no matching framework is
|
|
88
|
+
registered with the Picked client. Add the corresponding framework plugin to use it.
|
|
89
|
+
</Banner>
|
|
90
|
+
</div>
|
|
91
|
+
{/if}
|
|
92
|
+
|
|
93
|
+
<style>
|
|
94
|
+
.adapted-tool-host {
|
|
95
|
+
height: 100%;
|
|
96
|
+
padding: var(--space-4);
|
|
97
|
+
box-sizing: border-box;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.adapted-tool-fallback {
|
|
101
|
+
padding: var(--space-4);
|
|
102
|
+
}
|
|
103
|
+
</style>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AdaptedComponent, Picked } from '@picked/client';
|
|
2
|
+
/**
|
|
3
|
+
* A user-defined developer tool surfaced under the devtools "Custom Tools" tab.
|
|
4
|
+
*
|
|
5
|
+
* The component is framework-adapted (via `adaptReactComponent` or `adaptSvelteComponent`), so
|
|
6
|
+
* tools can be authored in the host app's framework and still receive the live Picked client the
|
|
7
|
+
* way an experience component does.
|
|
8
|
+
*/
|
|
9
|
+
export interface CustomTool {
|
|
10
|
+
id: string;
|
|
11
|
+
label: string;
|
|
12
|
+
component: AdaptedComponent;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Reads the custom tools registered with the devtools plugin for a given client.
|
|
16
|
+
*
|
|
17
|
+
* Malformed entries are dropped, and entries that repeat an already-seen `id` are dropped
|
|
18
|
+
* (first registration wins), so a single bad or duplicate registration can't break the tab — the
|
|
19
|
+
* `id` is used as a keyed-each key when the tools are rendered as tabs.
|
|
20
|
+
*
|
|
21
|
+
* @param {Picked} client - The Picked client whose plugin config holds the registered tools.
|
|
22
|
+
* @returns {CustomTool[]} The valid, id-unique registered custom tools, or an empty array when none exist.
|
|
23
|
+
* @example
|
|
24
|
+
* const tools = readCustomTools(getPicked())
|
|
25
|
+
*/
|
|
26
|
+
export declare function readCustomTools(client: Picked): CustomTool[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DEVTOOLS_PLUGIN_NAME as e}from"./plugin.js";function t(e){return typeof e==`object`&&!!e&&`frameworkName`in e&&typeof e.frameworkName==`string`&&`component`in e}function n(e){return typeof e==`object`&&!!e&&`id`in e&&typeof e.id==`string`&&`label`in e&&typeof e.label==`string`&&`component`in e&&t(e.component)}function r(t){let r=t.getPluginConfig(e).customTools;if(!Array.isArray(r))return[];let i=new Set;return r.filter(n).filter(e=>i.has(e.id)?!1:(i.add(e.id),!0))}export{r as readCustomTools};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Plugin } from '@picked/client';
|
|
2
|
+
import { CustomTool } from './customTools';
|
|
3
|
+
/** Plugin name under which the devtools plugin stores its config (including registered custom tools). */
|
|
4
|
+
export declare const DEVTOOLS_PLUGIN_NAME = "svelte-devtools";
|
|
2
5
|
export interface SvelteDevtoolsPluginOptions {
|
|
3
6
|
/**
|
|
4
7
|
* When `true`, the devtools experience launches automatically on page load.
|
|
@@ -9,6 +12,15 @@ export interface SvelteDevtoolsPluginOptions {
|
|
|
9
12
|
* @default false
|
|
10
13
|
*/
|
|
11
14
|
enabled?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Project-specific custom dev tools surfaced under the "Custom Tools" tab. Each tool's
|
|
17
|
+
* `component` is framework-adapted (via `adaptReactComponent` or `adaptSvelteComponent`), so it
|
|
18
|
+
* can be authored in the host app's framework and still receive the live Picked client the way
|
|
19
|
+
* an experience component does.
|
|
20
|
+
*
|
|
21
|
+
* @default []
|
|
22
|
+
*/
|
|
23
|
+
customTools?: CustomTool[];
|
|
12
24
|
}
|
|
13
25
|
/**
|
|
14
26
|
* Registers the devtools experience for on-demand loading via the Chrome DevTools extension.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{adaptSvelteComponent as e}from"../../component.js";import{__vitePreload as t}from"../../_virtual/_vite/preload-helper.js";import{lazyComponent as n}from"@picked/client";import{createExperience as r,createExperiencePlugin as i}from"@picked/client/plugins/create-experience";var a=
|
|
1
|
+
import{adaptSvelteComponent as e}from"../../component.js";import{__vitePreload as t}from"../../_virtual/_vite/preload-helper.js";import{lazyComponent as n}from"@picked/client";import{createExperience as r,createExperiencePlugin as i}from"@picked/client/plugins/create-experience";var a=`svelte-devtools`,o=o=>{let s=r({id:`devtools`,key:`devtools`,name:`Picked Devtools`,condition:!1,initializeOpen:!1,targets:[{selector:`body`,placement:`append`}],component:e(n(()=>t(()=>import(`./DevToolsExperience.svelte`),[])))});return s.addStep({name:`Tools`}),[{name:a,setup:e=>{let t=!!e.searchParams[`preview-token`]?.[0];return(o?.enabled||t)&&e.eventBus.watch(t=>{t.type===`ready`&&e.experiences.launch({key:s.key,options:{bypassCondition:!0}})}),{customTools:o?.customTools??[]}}},i({experiences:[s]})]};export{a as DEVTOOLS_PLUGIN_NAME,o as svelteDevtoolsPlugin};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/** @file Screen hosting user-registered custom devtools tools as nested tabs. */
|
|
3
|
+
import { getPicked } from '../../../../hooks'
|
|
4
|
+
import AdaptedToolHost from '../../components/AdaptedToolHost.svelte'
|
|
5
|
+
import Tabs from '../../components/Tabs.svelte'
|
|
6
|
+
import { readCustomTools } from '../../customTools'
|
|
7
|
+
import { Button, EmptyState, Stack } from '../../ui'
|
|
8
|
+
|
|
9
|
+
const client = getPicked()
|
|
10
|
+
const docsUrl = `${client.documentationSiteUrl}/chrome-extension#custom-tools`
|
|
11
|
+
const tools = readCustomTools(client)
|
|
12
|
+
const tabs = tools.map((tool) => ({
|
|
13
|
+
id: tool.id,
|
|
14
|
+
label: tool.label,
|
|
15
|
+
component: AdaptedToolHost,
|
|
16
|
+
props: { tool },
|
|
17
|
+
}))
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
{#if tabs.length > 0}
|
|
21
|
+
<Tabs {tabs} />
|
|
22
|
+
{:else}
|
|
23
|
+
<Stack direction="vertical" gap="medium" padding="medium" justify="center">
|
|
24
|
+
<EmptyState message="No custom tools registered." />
|
|
25
|
+
<Button variant="ghost" size="small" href={docsUrl} target="_blank">
|
|
26
|
+
Learn how to add custom tools
|
|
27
|
+
</Button>
|
|
28
|
+
</Stack>
|
|
29
|
+
{/if}
|
|
@@ -14,9 +14,8 @@ export interface SvelteFrameworkPluginOptions {
|
|
|
14
14
|
* This plugin registers the Svelte framework and allows rendering Svelte components
|
|
15
15
|
* with the client's experience data.
|
|
16
16
|
*
|
|
17
|
-
* @param {SvelteFrameworkPluginOptions} options - Configuration options for the Svelte plugin
|
|
17
|
+
* @param {SvelteFrameworkPluginOptions} options - Configuration options for the Svelte plugin.
|
|
18
18
|
* @returns {Plugin} The plugin.
|
|
19
|
-
*
|
|
20
19
|
* @example
|
|
21
20
|
* ```typescript
|
|
22
21
|
* createClient({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{adaptSvelteComponent as e}from"../../component.js";import{renderSvelteComponent as t}from"../../render/browser.js";var
|
|
1
|
+
import{adaptSvelteComponent as e}from"../../component.js";import{renderSvelteComponent as t,unRenderSvelteComponent as n}from"../../render/browser.js";var r=r=>({name:`svelte-framework`,setup:i=>{i.registerFramework({name:`svelte`,render(...e){return t(...e,{stylesheets:r.stylesheets,css:r.css})},unmount:e=>n(e)});let a=r.components.experience;a&&i.registerComponent(`experience`,e(a.component,a.style))}});export{r as svelteFrameworkPlugin};
|
package/dist/render/browser.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { AdaptedComponent, ExperienceInstance, Picked } from '@picked/client';
|
|
2
2
|
/**
|
|
3
|
-
* Builds the Svelte context map
|
|
3
|
+
* Builds the Svelte context map that exposes the client and experience to rendered components.
|
|
4
4
|
*
|
|
5
|
-
* @param {Picked} client - The Picked client.
|
|
6
|
-
* @param {ExperienceInstance} experience - The experience instance.
|
|
7
|
-
* @returns {Map<symbol, Picked | ExperienceInstance>} The context map keyed by the client and experience
|
|
5
|
+
* @param {Picked} client - The Picked client to expose via context.
|
|
6
|
+
* @param {ExperienceInstance} experience - The experience instance to expose via context.
|
|
7
|
+
* @returns {Map<symbol, Picked | ExperienceInstance>} The context map keyed by the client and experience symbols.
|
|
8
8
|
* @example
|
|
9
|
-
* createContext(client, experience)
|
|
9
|
+
* mount(Comp, { target, context: createContext(client, experience) })
|
|
10
10
|
*/
|
|
11
11
|
export declare function createContext(client: Picked, experience: ExperienceInstance): Map<symbol, Picked | ExperienceInstance>;
|
|
12
12
|
/**
|
|
@@ -28,3 +28,12 @@ export declare const renderSvelteComponent: (shadowRoot: ShadowRoot, component:
|
|
|
28
28
|
stylesheets?: string[];
|
|
29
29
|
css?: string[];
|
|
30
30
|
}) => Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Unmounts a Svelte component previously rendered into the given shadow root and clears its tracked instance.
|
|
33
|
+
*
|
|
34
|
+
* @param {ShadowRoot} target - The shadow root the component was rendered into.
|
|
35
|
+
* @returns {void} Nothing.
|
|
36
|
+
* @example
|
|
37
|
+
* unRenderSvelteComponent(host.shadowRoot)
|
|
38
|
+
*/
|
|
39
|
+
export declare const unRenderSvelteComponent: (target: ShadowRoot) => void;
|
package/dist/render/browser.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isValidAdaptedComponent as e}from"../component.js";import{CLIENT_CONTEXT_KEY as t,EXPERIENCE_CONTEXT_KEY as n}from"../context.js";import{mergeArrays as r}from"../utils/array.js";import{mount as i}from"svelte";import{isLazyLoader as
|
|
1
|
+
import{isValidAdaptedComponent as e}from"../component.js";import{CLIENT_CONTEXT_KEY as t,EXPERIENCE_CONTEXT_KEY as n}from"../context.js";import{mergeArrays as r}from"../utils/array.js";import{mount as i,unmount as a}from"svelte";import{isLazyLoader as o}from"@picked/client";import s from"../utils/ErrorBoundary.svelte";var c=new WeakMap;function l(e,r){return new Map([[t,e],[n,r]])}var u=async(t,n,a,u,d)=>{if(!e(n))throw Error(`The component is invalid.`);let f=r(n.style?.sheets,d.stylesheets),p=r(n.style?.css,d.css);f.length&&f.forEach(e=>{let n=document.createElement(`link`);n.setAttribute(`rel`,`stylesheet`),n.setAttribute(`href`,e),n.setAttribute(`type`,`text/css`),n.setAttribute(`picked-injected`,`true`),t.appendChild(n)}),p.length&&p.forEach(e=>{let n=document.createElement(`style`);n.textContent=e,n.setAttribute(`type`,`text/css`),n.setAttribute(`picked-injected`,`true`),t.appendChild(n)});let m=e=>{let n=i(s,{target:t,props:{component:e,componentProps:{client:a,experience:u}},context:l(a,u)});c.set(t,n)};if(o(n.component)){let{default:e}=await n.component.load();m(e);return}if(n.component instanceof Promise){let{default:e}=await n.component;m(e);return}m(n.component)},d=e=>{let t=c.get(e);t&&(a(t),c.delete(e))};export{l as createContext,u as renderSvelteComponent,d as unRenderSvelteComponent};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@picked/svelte",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.64-next.2",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"d3": "^7.9.0"
|
|
6
6
|
},
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
},
|
|
107
107
|
"module": "./dist/index.js",
|
|
108
108
|
"peerDependencies": {
|
|
109
|
-
"@picked/client": "0.0.
|
|
109
|
+
"@picked/client": "0.0.64-next.2",
|
|
110
110
|
"effector": "^23.3.0",
|
|
111
111
|
"svelte": "^5.46.4"
|
|
112
112
|
},
|