mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-22 16:02:24 +00:00
WIP: unstable type handler
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
import { offset, flip, shift } from 'svelte-floating-ui/dom'
|
||||
import ResolveStyle from '../helpers/ResolveStyle.svelte'
|
||||
import MultiSelect from '$lib/components/multiselect/MultiSelect.svelte'
|
||||
import type { ObjectOption } from '../../../multiselect/types'
|
||||
|
||||
export let id: string
|
||||
export let configuration: RichConfigurations
|
||||
@@ -23,6 +24,11 @@
|
||||
export let render: boolean
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
|
||||
// every option is labeled, or no one is.
|
||||
type Options = ObjectOption[] | string[]
|
||||
|
||||
$: resolvedConfig.items && handleItems()
|
||||
|
||||
const [floatingRef, floatingContent] = createFloatingActions({
|
||||
strategy: 'absolute',
|
||||
middleware: [offset(5), flip(), shift()]
|
||||
@@ -30,7 +36,7 @@
|
||||
|
||||
const { app, worldStore, selectedComponent, componentControl } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
let items: { label: string; value: string; created?: boolean }[]
|
||||
let items: Options = []
|
||||
|
||||
const resolvedConfig = initConfig(
|
||||
components['multiselectcomponentv2'].initialData.configuration,
|
||||
@@ -38,59 +44,110 @@
|
||||
)
|
||||
|
||||
const outputs = initOutput($worldStore, id, {
|
||||
result: [] as { label: string; value: string; created?: boolean }[]
|
||||
result: [] as Options
|
||||
})
|
||||
|
||||
let value: { label: string; value: string; created?: boolean }[] | undefined = [
|
||||
...new Set(outputs?.result.peak())
|
||||
] as { label: string; value: string; created?: boolean }[]
|
||||
let value: Options | undefined = isLabeledArray(outputs?.result.peak())
|
||||
? ([...new Set(outputs?.result.peak())] as ObjectOption[])
|
||||
: ([...new Set(outputs?.result.peak())] as string[])
|
||||
|
||||
$componentControl[id] = {
|
||||
setValue(nvalue: { label: string; value: string; created?: boolean }[]) {
|
||||
value = [...new Set(nvalue)]
|
||||
outputs?.result.set([...(value ?? [])])
|
||||
setValue(nvalue: Options) {
|
||||
if (isLabeledArray(nvalue)) {
|
||||
value = [...new Set(nvalue)] as ObjectOption[]
|
||||
outputs?.result.set([...(value ?? [])])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: resolvedConfig.items && handleItems()
|
||||
function isObjectOption(item: Options[number]): item is ObjectOption {
|
||||
if (typeof item === 'string') {
|
||||
return false
|
||||
}
|
||||
if (item.label === undefined || item.label === null) {
|
||||
return false
|
||||
}
|
||||
if (typeof item.label !== 'string' && typeof item.label !== 'number') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function handleItems() {
|
||||
function isLabeledArray(arr: Options): arr is ObjectOption[] {
|
||||
return arr.every((item) => isObjectOption(item))
|
||||
}
|
||||
|
||||
function isStringArray(arr: Options): arr is string[] {
|
||||
return arr.every((item) => typeof item === 'string')
|
||||
}
|
||||
|
||||
function handleLabeledItems() {
|
||||
if (!Array.isArray(resolvedConfig.items)) {
|
||||
items = []
|
||||
} else {
|
||||
items = resolvedConfig.items?.map((item) => {
|
||||
if (!item || typeof item !== 'object') {
|
||||
console.error('Select component items should be an array of objects')
|
||||
return {
|
||||
label: 'not object',
|
||||
value: 'not object'
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
items = resolvedConfig.items?.map((item) => {
|
||||
if (!item || typeof item !== 'object') {
|
||||
console.error(
|
||||
'When labeled, MultiSelect component items should be an array of { label: string, value: string }.'
|
||||
)
|
||||
return {
|
||||
label: item?.label ?? 'undefined',
|
||||
value:
|
||||
typeof item?.value === 'object'
|
||||
? JSON.stringify(item.value)
|
||||
: item?.value ?? 'undefined'
|
||||
label: 'not object',
|
||||
value: 'not object'
|
||||
}
|
||||
})
|
||||
}
|
||||
return {
|
||||
label: item?.label ?? 'undefined',
|
||||
value:
|
||||
typeof item?.value === 'object' ? JSON.stringify(item.value) : item?.value ?? 'undefined'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleStringItems() {
|
||||
if (!Array.isArray(resolvedConfig.items)) {
|
||||
items = []
|
||||
return
|
||||
}
|
||||
items = resolvedConfig.items?.map((item) => {
|
||||
if (!item || typeof item !== 'string') {
|
||||
console.error(
|
||||
'When not labeled, MultiSelect component items should be an array of strings.'
|
||||
)
|
||||
return 'not string'
|
||||
}
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
function handleItems() {
|
||||
if (isLabeledArray(resolvedConfig.items)) {
|
||||
handleLabeledItems()
|
||||
} else if (isStringArray(resolvedConfig.items)) {
|
||||
handleStringItems()
|
||||
}
|
||||
}
|
||||
|
||||
$: resolvedConfig.defaultItems && handleDefaultItems()
|
||||
|
||||
// todo
|
||||
function handleDefaultItems() {
|
||||
let nvalue: { label: string; value: string; created?: boolean }[]
|
||||
let nvalue: typeof items
|
||||
if (!Array.isArray(resolvedConfig.defaultItems)) {
|
||||
nvalue = []
|
||||
} else {
|
||||
let rawNvalue = new Set(
|
||||
resolvedConfig.defaultItems?.filter((value) => typeof value === 'string')
|
||||
)
|
||||
nvalue = items?.filter((item) => rawNvalue.has(item.value))
|
||||
outputs?.result.set([])
|
||||
return
|
||||
}
|
||||
let rawNvalue = new Set(resolvedConfig.defaultItems?.filter((v) => typeof v === 'string'))
|
||||
if (isLabeledArray(items)) {
|
||||
nvalue = items?.filter((item) => rawNvalue.has(item.value)) as ObjectOption[]
|
||||
value = [...new Set(nvalue)]
|
||||
outputs?.result.set([...(value ?? [])])
|
||||
} else if (isStringArray(items)) {
|
||||
nvalue = items?.filter((label) => rawNvalue.has(label)) as string[]
|
||||
value = [...new Set(nvalue)]
|
||||
outputs?.result.set([...(value ?? [])])
|
||||
}
|
||||
value = [...new Set(nvalue)]
|
||||
outputs?.result.set([...(value ?? [])])
|
||||
}
|
||||
|
||||
let css = initCss($app.css?.multiselectcomponent, customCss)
|
||||
|
||||
@@ -2203,12 +2203,13 @@ This is a paragraph.
|
||||
value: [
|
||||
{ value: 'foo', label: 'Foo' },
|
||||
{ value: 'bar', label: 'Bar' }
|
||||
]
|
||||
],
|
||||
hasLabeledMode: true
|
||||
} as StaticAppInput,
|
||||
defaultItems: {
|
||||
type: 'static',
|
||||
fieldType: 'array',
|
||||
subFieldType: 'selectvalue',
|
||||
subFieldType: 'simplestringselect',
|
||||
value: undefined
|
||||
} as StaticAppInput,
|
||||
placeholder: {
|
||||
|
||||
+36
-5
@@ -2,7 +2,7 @@
|
||||
import { Button } from '$lib/components/common'
|
||||
import { GripVertical, Loader2, Plus, X } from 'lucide-svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import type { InputType, StaticInput, StaticOptions } from '../../inputType'
|
||||
import type { InputType, LabeledOption, StaticInput, StaticOptions } from '../../inputType'
|
||||
import SubTypeEditor from './SubTypeEditor.svelte'
|
||||
import { dragHandle, dragHandleZone } from '@windmill-labs/svelte-dnd-action'
|
||||
import { generateRandomString, pluralize } from '$lib/utils'
|
||||
@@ -10,14 +10,26 @@
|
||||
import QuickAddColumn from './QuickAddColumn.svelte'
|
||||
import RefreshDatabaseStudioTable from './RefreshDatabaseStudioTable.svelte'
|
||||
|
||||
export let componentInput: StaticInput<any[]> & { loading?: boolean }
|
||||
export let componentInput: StaticInput<any[]> & { loading?: boolean; isLabeled?: boolean }
|
||||
export let subFieldType: InputType | undefined = undefined
|
||||
export let selectOptions: StaticOptions['selectOptions'] | undefined = undefined
|
||||
export let id: string | undefined
|
||||
export let hasLabeledMode: boolean = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const flipDurationMs = 200
|
||||
|
||||
console.log(componentInput.value)
|
||||
|
||||
// transform string[] to LabeledOption[], or the opposite way
|
||||
function handleLabeledChange() {
|
||||
if (labeled) {
|
||||
componentInput.value = componentInput.value?.map((option: LabeledOption) => option?.label)
|
||||
} else if (!labeled) {
|
||||
componentInput.value = componentInput.value?.map((label) => ({ label, value: label }))
|
||||
}
|
||||
}
|
||||
|
||||
function addElementByType() {
|
||||
if (!Array.isArray(componentInput.value)) {
|
||||
componentInput.value = []
|
||||
@@ -33,13 +45,12 @@
|
||||
value.push({})
|
||||
} else if (subFieldType === 'labeledresource' || subFieldType === 'labeledselect') {
|
||||
value.push({ value: 'value', label: 'label' })
|
||||
} else if (subFieldType === 'selectvalue') {
|
||||
value.push('')
|
||||
} else if (subFieldType === 'tab-select') {
|
||||
value.push({ id: '', index: 0 })
|
||||
} else if (
|
||||
subFieldType === 'text' ||
|
||||
subFieldType === 'textarea' ||
|
||||
subFieldType === 'simplestringselect' ||
|
||||
// TODO: Add support for these types
|
||||
subFieldType === 'date' ||
|
||||
subFieldType === 'time' ||
|
||||
@@ -220,6 +231,10 @@
|
||||
}
|
||||
|
||||
let raw: boolean = false
|
||||
let labeled: boolean = false
|
||||
|
||||
$: labeled
|
||||
|
||||
// let mounted = false
|
||||
|
||||
// $: if (componentInput.value && mounted) {
|
||||
@@ -243,6 +258,12 @@
|
||||
|
||||
<div class="flex gap-2 flex-col mt-2 w-full">
|
||||
{#if Array.isArray(items) && componentInput.value}
|
||||
<!-- {#if componentInput.isLabeled}<Toggle
|
||||
size="xs"
|
||||
options={{ right: 'LabeledOption' }}
|
||||
bind:checked={isLabeled}
|
||||
/>
|
||||
{/if} -->
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<div class="text-xs text-tertiary font-semibold">{pluralize(items.length, 'item')}</div>
|
||||
|
||||
@@ -255,6 +276,16 @@
|
||||
bind:checked={raw}
|
||||
/>
|
||||
{/if}
|
||||
{#if hasLabeledMode}
|
||||
<Toggle
|
||||
options={{
|
||||
right: 'Labeled'
|
||||
}}
|
||||
size="xs"
|
||||
bind:checked={labeled}
|
||||
on:change={handleLabeledChange}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<section
|
||||
use:dragHandleZone={{
|
||||
@@ -273,7 +304,7 @@
|
||||
<div class="grow min-w-0">
|
||||
<SubTypeEditor
|
||||
{id}
|
||||
subFieldType={raw ? 'object' : subFieldType}
|
||||
subFieldType={raw ? 'object' : !labeled ? 'simplestringselect' : subFieldType}
|
||||
bind:componentInput
|
||||
bind:value={item.value}
|
||||
on:remove={() => deleteElementByType(index)}
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
export let documentationLink: string | undefined = undefined
|
||||
export let markdownTooltip: string | undefined = undefined
|
||||
export let securedContext = false
|
||||
export let hasLabeledMode = false
|
||||
|
||||
const { connectingInput, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
@@ -171,6 +172,7 @@
|
||||
{selectOptions}
|
||||
{format}
|
||||
{placeholder}
|
||||
{hasLabeledMode}
|
||||
bind:componentInput
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
{recomputeOnInputChanged}
|
||||
{showOnDemandOnlyToggle}
|
||||
{securedContext}
|
||||
hasLabeledMode={meta?.['hasLabeledMode']}
|
||||
/>
|
||||
{#if deletable}
|
||||
<div class="flex flex-row-reverse -mt-4">
|
||||
|
||||
+9
-10
@@ -31,6 +31,7 @@
|
||||
export let placeholder: string | undefined = undefined
|
||||
export let format: string | undefined = undefined
|
||||
export let id: string | undefined
|
||||
export let hasLabeledMode: boolean = false
|
||||
|
||||
const { onchange } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
@@ -133,7 +134,7 @@
|
||||
{/if}
|
||||
{:else if fieldType === 'color'}
|
||||
<ColorInput bind:value={componentInput.value} />
|
||||
{:else if fieldType === 'object' || fieldType == 'labeledselect'}
|
||||
{:else if fieldType === 'object' || fieldType == 'labeledselect' || fieldType === 'simplestringselect'}
|
||||
{#if format?.startsWith('resource-') && (componentInput.value == undefined || typeof componentInput.value == 'string')}
|
||||
<ResourcePicker
|
||||
initialValue={componentInput.value?.split('$res:')?.[1] || ''}
|
||||
@@ -161,16 +162,14 @@
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{:else if fieldType === 'selectvalue'}
|
||||
<div class="flex w-full flex-col">
|
||||
<JsonEditor
|
||||
small
|
||||
bind:value={componentInput.value}
|
||||
code={JSON.stringify(componentInput.value, null, 2)}
|
||||
/>
|
||||
</div>
|
||||
{:else if fieldType === 'array'}
|
||||
<ArrayStaticInputEditor {id} {subFieldType} bind:componentInput on:deleteArrayItem />
|
||||
<ArrayStaticInputEditor
|
||||
{id}
|
||||
{subFieldType}
|
||||
bind:componentInput
|
||||
on:deleteArrayItem
|
||||
{hasLabeledMode}
|
||||
/>
|
||||
{:else if fieldType === 'schema'}
|
||||
<div class="w-full">
|
||||
<EditableSchemaDrawer bind:schema={componentInput.value} />
|
||||
|
||||
@@ -20,7 +20,7 @@ export type InputType =
|
||||
| 'any'
|
||||
| 'labeledresource'
|
||||
| 'labeledselect'
|
||||
| 'selectvalue'
|
||||
| 'simplestringselect'
|
||||
| 'tab-select'
|
||||
| 'schema'
|
||||
| 'ag-grid'
|
||||
@@ -176,6 +176,7 @@ type InputConfiguration<T extends InputType, V extends InputType> = {
|
||||
noStatic?: boolean
|
||||
onDemandOnly?: boolean
|
||||
hideRefreshButton?: boolean
|
||||
isLabeled?: boolean
|
||||
}
|
||||
|
||||
export type StaticOptions = {
|
||||
@@ -208,7 +209,7 @@ export type AppInput =
|
||||
| (AppInputSpec<'array', string[], 'select'> & StaticOptions)
|
||||
| AppInputSpec<'array', object[], 'labeledresource'>
|
||||
| AppInputSpec<'array', object[], 'labeledselect'>
|
||||
| AppInputSpec<'array', object[], 'selectvalue'>
|
||||
| AppInputSpec<'array', object[], 'simplestringselect'>
|
||||
| AppInputSpec<'labeledselect', object>
|
||||
| AppInputSpec<'labeledresource', object>
|
||||
| AppInputSpec<'array', object[], 'tab-select'>
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// vite.config.js
|
||||
import { sveltekit } from "file:///home/guest-windmill/windmill/frontend/node_modules/@sveltejs/kit/src/exports/vite/index.js";
|
||||
import { readFileSync } from "fs";
|
||||
import { fileURLToPath } from "url";
|
||||
import circleDependency from "file:///home/guest-windmill/windmill/frontend/node_modules/vite-plugin-circular-dependency/dist/index.mjs";
|
||||
import importMetaUrlPlugin from "file:///home/guest-windmill/windmill/frontend/node_modules/@windmill-labs/esbuild-import-meta-url-plugin/dist/esbuildImportMetaUrlPlugin.js";
|
||||
var __vite_injected_original_import_meta_url = "file:///home/guest-windmill/windmill/frontend/vite.config.js";
|
||||
var file = fileURLToPath(new URL("package.json", __vite_injected_original_import_meta_url));
|
||||
var json = readFileSync(file, "utf8");
|
||||
var version = JSON.parse(json);
|
||||
var config = {
|
||||
server: {
|
||||
https: false,
|
||||
port: 3e3,
|
||||
proxy: {
|
||||
"^/api/.*": {
|
||||
target: process.env.REMOTE ?? "https://app.windmill.dev/",
|
||||
changeOrigin: true,
|
||||
cookieDomainRewrite: "localhost"
|
||||
},
|
||||
"^/ws/.*": {
|
||||
target: process.env.REMOTE_LSP ?? "https://app.windmill.dev",
|
||||
changeOrigin: true,
|
||||
ws: true
|
||||
},
|
||||
"^/ws_mp/.*": {
|
||||
target: process.env.REMOTE_MP ?? "https://app.windmill.dev",
|
||||
changeOrigin: true,
|
||||
ws: true
|
||||
}
|
||||
}
|
||||
},
|
||||
preview: {
|
||||
port: 3e3
|
||||
},
|
||||
plugins: [sveltekit(), circleDependency({ circleImportThrowErr: false })],
|
||||
define: {
|
||||
__pkg__: version
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ["highlight.js", "highlight.js/lib/core", "monaco-vim"],
|
||||
exclude: [
|
||||
"@codingame/monaco-vscode-standalone-typescript-language-features",
|
||||
"@codingame/monaco-vscode-standalone-languages",
|
||||
"monaco-graphql"
|
||||
],
|
||||
esbuildOptions: {
|
||||
plugins: [importMetaUrlPlugin]
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
path: "path-browserify",
|
||||
"vscode/vscode/vs/editor/contrib/hover/browser/hover": "vscode/vscode/vs/editor/contrib/hover/browser/hoverController"
|
||||
},
|
||||
dedupe: ["vscode", "monaco-editor"]
|
||||
},
|
||||
assetsInclude: ["**/*.wasm"]
|
||||
};
|
||||
var vite_config_default = config;
|
||||
export {
|
||||
vite_config_default as default
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvaG9tZS9ndWVzdC13aW5kbWlsbC93aW5kbWlsbC9mcm9udGVuZFwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiL2hvbWUvZ3Vlc3Qtd2luZG1pbGwvd2luZG1pbGwvZnJvbnRlbmQvdml0ZS5jb25maWcuanNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL2hvbWUvZ3Vlc3Qtd2luZG1pbGwvd2luZG1pbGwvZnJvbnRlbmQvdml0ZS5jb25maWcuanNcIjtpbXBvcnQgeyBzdmVsdGVraXQgfSBmcm9tICdAc3ZlbHRlanMva2l0L3ZpdGUnXG5pbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tICdmcydcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tICd1cmwnXG5pbXBvcnQgY2lyY2xlRGVwZW5kZW5jeSBmcm9tICd2aXRlLXBsdWdpbi1jaXJjdWxhci1kZXBlbmRlbmN5J1xuLy8gaW1wb3J0IG1rY2VydCBmcm9tICd2aXRlLXBsdWdpbi1ta2NlcnQnXG5pbXBvcnQgaW1wb3J0TWV0YVVybFBsdWdpbiBmcm9tICdAd2luZG1pbGwtbGFicy9lc2J1aWxkLWltcG9ydC1tZXRhLXVybC1wbHVnaW4nXG5cbmNvbnN0IGZpbGUgPSBmaWxlVVJMVG9QYXRoKG5ldyBVUkwoJ3BhY2thZ2UuanNvbicsIGltcG9ydC5tZXRhLnVybCkpXG5jb25zdCBqc29uID0gcmVhZEZpbGVTeW5jKGZpbGUsICd1dGY4JylcbmNvbnN0IHZlcnNpb24gPSBKU09OLnBhcnNlKGpzb24pXG5cbi8qKiBAdHlwZSB7aW1wb3J0KCd2aXRlJykuVXNlckNvbmZpZ30gKi9cbmNvbnN0IGNvbmZpZyA9IHtcblx0c2VydmVyOiB7XG5cdFx0aHR0cHM6IGZhbHNlLFxuXHRcdHBvcnQ6IDMwMDAsXG5cdFx0cHJveHk6IHtcblx0XHRcdCdeL2FwaS8uKic6IHtcblx0XHRcdFx0dGFyZ2V0OiBwcm9jZXNzLmVudi5SRU1PVEUgPz8gJ2h0dHBzOi8vYXBwLndpbmRtaWxsLmRldi8nLFxuXHRcdFx0XHRjaGFuZ2VPcmlnaW46IHRydWUsXG5cdFx0XHRcdGNvb2tpZURvbWFpblJld3JpdGU6ICdsb2NhbGhvc3QnXG5cdFx0XHR9LFxuXHRcdFx0J14vd3MvLionOiB7XG5cdFx0XHRcdHRhcmdldDogcHJvY2Vzcy5lbnYuUkVNT1RFX0xTUCA/PyAnaHR0cHM6Ly9hcHAud2luZG1pbGwuZGV2Jyxcblx0XHRcdFx0Y2hhbmdlT3JpZ2luOiB0cnVlLFxuXHRcdFx0XHR3czogdHJ1ZVxuXHRcdFx0fSxcblx0XHRcdCdeL3dzX21wLy4qJzoge1xuXHRcdFx0XHR0YXJnZXQ6IHByb2Nlc3MuZW52LlJFTU9URV9NUCA/PyAnaHR0cHM6Ly9hcHAud2luZG1pbGwuZGV2Jyxcblx0XHRcdFx0Y2hhbmdlT3JpZ2luOiB0cnVlLFxuXHRcdFx0XHR3czogdHJ1ZVxuXHRcdFx0fVxuXHRcdH1cblx0fSxcblx0cHJldmlldzoge1xuXHRcdHBvcnQ6IDMwMDBcblx0fSxcblx0cGx1Z2luczogW3N2ZWx0ZWtpdCgpLCBjaXJjbGVEZXBlbmRlbmN5KHsgY2lyY2xlSW1wb3J0VGhyb3dFcnI6IGZhbHNlIH0pXSxcblx0ZGVmaW5lOiB7XG5cdFx0X19wa2dfXzogdmVyc2lvblxuXHR9LFxuXHRvcHRpbWl6ZURlcHM6IHtcblx0XHRpbmNsdWRlOiBbJ2hpZ2hsaWdodC5qcycsICdoaWdobGlnaHQuanMvbGliL2NvcmUnLCAnbW9uYWNvLXZpbSddLFxuXHRcdGV4Y2x1ZGU6IFtcblx0XHRcdCdAY29kaW5nYW1lL21vbmFjby12c2NvZGUtc3RhbmRhbG9uZS10eXBlc2NyaXB0LWxhbmd1YWdlLWZlYXR1cmVzJyxcblx0XHRcdCdAY29kaW5nYW1lL21vbmFjby12c2NvZGUtc3RhbmRhbG9uZS1sYW5ndWFnZXMnLFxuXHRcdFx0J21vbmFjby1ncmFwaHFsJ1xuXHRcdF0sXG5cdFx0ZXNidWlsZE9wdGlvbnM6IHtcblx0XHRcdHBsdWdpbnM6IFtpbXBvcnRNZXRhVXJsUGx1Z2luXVxuXHRcdH1cblx0fSxcblx0cmVzb2x2ZToge1xuXHRcdGFsaWFzOiB7XG5cdFx0XHRwYXRoOiAncGF0aC1icm93c2VyaWZ5Jyxcblx0XHRcdCd2c2NvZGUvdnNjb2RlL3ZzL2VkaXRvci9jb250cmliL2hvdmVyL2Jyb3dzZXIvaG92ZXInOlxuXHRcdFx0XHQndnNjb2RlL3ZzY29kZS92cy9lZGl0b3IvY29udHJpYi9ob3Zlci9icm93c2VyL2hvdmVyQ29udHJvbGxlcidcblx0XHR9LFxuXHRcdGRlZHVwZTogWyd2c2NvZGUnLCAnbW9uYWNvLWVkaXRvciddXG5cdH0sXG5cdGFzc2V0c0luY2x1ZGU6IFsnKiovKi53YXNtJ11cbn1cblxuZXhwb3J0IGRlZmF1bHQgY29uZmlnXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQW9TLFNBQVMsaUJBQWlCO0FBQzlULFNBQVMsb0JBQW9CO0FBQzdCLFNBQVMscUJBQXFCO0FBQzlCLE9BQU8sc0JBQXNCO0FBRTdCLE9BQU8seUJBQXlCO0FBTG9KLElBQU0sMkNBQTJDO0FBT3JPLElBQU0sT0FBTyxjQUFjLElBQUksSUFBSSxnQkFBZ0Isd0NBQWUsQ0FBQztBQUNuRSxJQUFNLE9BQU8sYUFBYSxNQUFNLE1BQU07QUFDdEMsSUFBTSxVQUFVLEtBQUssTUFBTSxJQUFJO0FBRy9CLElBQU0sU0FBUztBQUFBLEVBQ2QsUUFBUTtBQUFBLElBQ1AsT0FBTztBQUFBLElBQ1AsTUFBTTtBQUFBLElBQ04sT0FBTztBQUFBLE1BQ04sWUFBWTtBQUFBLFFBQ1gsUUFBUSxRQUFRLElBQUksVUFBVTtBQUFBLFFBQzlCLGNBQWM7QUFBQSxRQUNkLHFCQUFxQjtBQUFBLE1BQ3RCO0FBQUEsTUFDQSxXQUFXO0FBQUEsUUFDVixRQUFRLFFBQVEsSUFBSSxjQUFjO0FBQUEsUUFDbEMsY0FBYztBQUFBLFFBQ2QsSUFBSTtBQUFBLE1BQ0w7QUFBQSxNQUNBLGNBQWM7QUFBQSxRQUNiLFFBQVEsUUFBUSxJQUFJLGFBQWE7QUFBQSxRQUNqQyxjQUFjO0FBQUEsUUFDZCxJQUFJO0FBQUEsTUFDTDtBQUFBLElBQ0Q7QUFBQSxFQUNEO0FBQUEsRUFDQSxTQUFTO0FBQUEsSUFDUixNQUFNO0FBQUEsRUFDUDtBQUFBLEVBQ0EsU0FBUyxDQUFDLFVBQVUsR0FBRyxpQkFBaUIsRUFBRSxzQkFBc0IsTUFBTSxDQUFDLENBQUM7QUFBQSxFQUN4RSxRQUFRO0FBQUEsSUFDUCxTQUFTO0FBQUEsRUFDVjtBQUFBLEVBQ0EsY0FBYztBQUFBLElBQ2IsU0FBUyxDQUFDLGdCQUFnQix5QkFBeUIsWUFBWTtBQUFBLElBQy9ELFNBQVM7QUFBQSxNQUNSO0FBQUEsTUFDQTtBQUFBLE1BQ0E7QUFBQSxJQUNEO0FBQUEsSUFDQSxnQkFBZ0I7QUFBQSxNQUNmLFNBQVMsQ0FBQyxtQkFBbUI7QUFBQSxJQUM5QjtBQUFBLEVBQ0Q7QUFBQSxFQUNBLFNBQVM7QUFBQSxJQUNSLE9BQU87QUFBQSxNQUNOLE1BQU07QUFBQSxNQUNOLHVEQUNDO0FBQUEsSUFDRjtBQUFBLElBQ0EsUUFBUSxDQUFDLFVBQVUsZUFBZTtBQUFBLEVBQ25DO0FBQUEsRUFDQSxlQUFlLENBQUMsV0FBVztBQUM1QjtBQUVBLElBQU8sc0JBQVE7IiwKICAibmFtZXMiOiBbXQp9Cg==
|
||||
Reference in New Issue
Block a user