@openforge-app/plugin-sdk 0.1.0 → 0.2.4
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/LICENSE +21 -0
- package/README.md +26 -0
- package/dist/backend.d.ts +2 -2
- package/dist/browserSurfaces.d.ts +78 -0
- package/dist/browserSurfaces.js +17 -0
- package/dist/browserSurfacesTesting.d.ts +60 -0
- package/dist/browserSurfacesTesting.js +240 -0
- package/dist/domain.d.ts +74 -15
- package/dist/domain.js +5 -1
- package/dist/fileIcons.d.ts +6 -0
- package/dist/fileIcons.js +84 -0
- package/dist/frontend.d.ts +5 -2
- package/dist/frontend.js +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +2 -1
- package/dist/manifest.d.ts +80 -2
- package/dist/manifest.js +55 -1
- package/dist/markdown.d.ts +5 -1
- package/dist/markdown.js +67 -18
- package/dist/openforgePackageMetadataSchema.json +80 -4
- package/dist/pluginIconContract.d.ts +3 -0
- package/dist/pluginIconContract.js +12 -0
- package/dist/pluginIcons.d.ts +7 -0
- package/dist/pluginIcons.js +99 -0
- package/dist/publicUiExports.d.mts +16 -0
- package/dist/publicUiExports.mjs +60 -0
- package/dist/svelteHostRuntimeContract.d.mts +1 -0
- package/dist/svelteHostRuntimeContract.mjs +16 -0
- package/dist/testing/backendServicesFake.d.ts +19 -0
- package/dist/testing/backendServicesFake.js +87 -0
- package/dist/testing/commonApiFake.d.ts +23 -0
- package/dist/testing/commonApiFake.js +306 -0
- package/dist/testing/contracts.d.ts +236 -0
- package/dist/testing/contracts.js +1 -0
- package/dist/testing/frontendContributionFake.d.ts +36 -0
- package/dist/testing/frontendContributionFake.js +204 -0
- package/dist/testing/registryFake.d.ts +40 -0
- package/dist/testing/registryFake.js +124 -0
- package/dist/testing/support.d.ts +40 -0
- package/dist/testing/support.js +247 -0
- package/dist/testing.d.ts +3 -223
- package/dist/testing.js +2 -625
- package/dist/types.d.ts +251 -9
- package/dist/types.js +47 -0
- package/dist/ui/Button.svelte +49 -0
- package/dist/ui/Checkbox.svelte +137 -0
- package/dist/ui/FileTypeIcon.svelte +37 -0
- package/dist/ui/MarkdownContent.svelte +71 -11
- package/dist/ui/Modal.svelte +157 -0
- package/dist/ui/PluginPageHeader.svelte +26 -0
- package/dist/ui/PluginSidebarLink.svelte +54 -0
- package/dist/ui/PluginViewState.svelte +76 -0
- package/dist/ui/ResizablePanel.svelte +10 -0
- package/dist/ui/icons/README.md +9 -0
- package/dist/ui/icons/c.svg +1 -0
- package/dist/ui/icons/console.svg +1 -0
- package/dist/ui/icons/cpp.svg +1 -0
- package/dist/ui/icons/csharp.svg +1 -0
- package/dist/ui/icons/css.svg +3 -0
- package/dist/ui/icons/database.svg +1 -0
- package/dist/ui/icons/docker.svg +1 -0
- package/dist/ui/icons/document.svg +4 -0
- package/dist/ui/icons/file.svg +1 -0
- package/dist/ui/icons/folder-open.svg +1 -0
- package/dist/ui/icons/folder.svg +1 -0
- package/dist/ui/icons/git.svg +3 -0
- package/dist/ui/icons/go.svg +1 -0
- package/dist/ui/icons/graphql.svg +20 -0
- package/dist/ui/icons/h.svg +3 -0
- package/dist/ui/icons/html.svg +3 -0
- package/dist/ui/icons/image.svg +3 -0
- package/dist/ui/icons/java.svg +6 -0
- package/dist/ui/icons/javascript.svg +3 -0
- package/dist/ui/icons/json.svg +3 -0
- package/dist/ui/icons/kotlin.svg +1 -0
- package/dist/ui/icons/lock.svg +3 -0
- package/dist/ui/icons/markdown.svg +6 -0
- package/dist/ui/icons/nodejs.svg +6 -0
- package/dist/ui/icons/npm.svg +3 -0
- package/dist/ui/icons/pdf.svg +1 -0
- package/dist/ui/icons/php.svg +1 -0
- package/dist/ui/icons/python.svg +1 -0
- package/dist/ui/icons/react.svg +8 -0
- package/dist/ui/icons/react_ts.svg +8 -0
- package/dist/ui/icons/readme.svg +4 -0
- package/dist/ui/icons/ruby.svg +1 -0
- package/dist/ui/icons/rust.svg +1 -0
- package/dist/ui/icons/sass.svg +3 -0
- package/dist/ui/icons/settings.svg +4 -0
- package/dist/ui/icons/svelte.svg +1 -0
- package/dist/ui/icons/svg.svg +3 -0
- package/dist/ui/icons/swift.svg +1 -0
- package/dist/ui/icons/tune.svg +11 -0
- package/dist/ui/icons/typescript-def.svg +6 -0
- package/dist/ui/icons/typescript.svg +3 -0
- package/dist/ui/icons/vue.svg +1 -0
- package/dist/ui/icons/xml.svg +1 -0
- package/dist/ui/icons/yaml.svg +3 -0
- package/dist/ui/icons/zip.svg +3 -0
- package/dist/vite.js +5 -2
- package/package.json +35 -7
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getFileIconName, getFolderIconName } from '../fileIcons'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
filename?: string
|
|
6
|
+
folder?: boolean
|
|
7
|
+
open?: boolean
|
|
8
|
+
class?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { filename, folder = false, open = false, class: className = '' }: Props = $props()
|
|
12
|
+
|
|
13
|
+
// Curated, vendored set only (~45 files) — eager raw is fine and avoids runtime asset URLs.
|
|
14
|
+
const icons = import.meta.glob('./icons/*.svg', {
|
|
15
|
+
query: '?raw',
|
|
16
|
+
import: 'default',
|
|
17
|
+
eager: true,
|
|
18
|
+
}) as Record<string, string>
|
|
19
|
+
|
|
20
|
+
let iconName = $derived(folder ? getFolderIconName(open) : getFileIconName(filename ?? ''))
|
|
21
|
+
let svg = $derived(icons[`./icons/${iconName}.svg`] ?? icons['./icons/file.svg'] ?? '')
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<span
|
|
25
|
+
class="file-type-icon inline-flex items-center justify-center shrink-0 {className}"
|
|
26
|
+
data-icon={iconName}
|
|
27
|
+
aria-hidden="true"
|
|
28
|
+
>
|
|
29
|
+
{@html svg}
|
|
30
|
+
</span>
|
|
31
|
+
|
|
32
|
+
<style>
|
|
33
|
+
.file-type-icon :global(svg) {
|
|
34
|
+
width: 100%;
|
|
35
|
+
height: 100%;
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
@@ -1,30 +1,90 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { onDestroy } from 'svelte'
|
|
3
|
+
import { getMarkdownRepositoryLinkSuffix, renderMarkdownHtml, resolveMarkdownRepositoryPath } from '../markdown'
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
content: string
|
|
6
7
|
imageBaseUrl?: string | null
|
|
8
|
+
markdownFilePath?: string | null
|
|
9
|
+
resolveRepositoryImage?: (repositoryPath: string) => Promise<string | null>
|
|
10
|
+
onOpenRepositoryPath?: (repositoryPath: string, suffix: string) => void | Promise<void>
|
|
7
11
|
onOpenUrl?: (url: string) => void | Promise<void>
|
|
8
12
|
}
|
|
9
13
|
|
|
10
|
-
let {
|
|
14
|
+
let {
|
|
15
|
+
content,
|
|
16
|
+
imageBaseUrl = null,
|
|
17
|
+
markdownFilePath = null,
|
|
18
|
+
resolveRepositoryImage,
|
|
19
|
+
onOpenRepositoryPath,
|
|
20
|
+
onOpenUrl,
|
|
21
|
+
}: Props = $props()
|
|
11
22
|
|
|
12
|
-
let
|
|
23
|
+
let root = $state<HTMLDivElement | null>(null)
|
|
24
|
+
let imageResolutionId = 0
|
|
25
|
+
let html = $derived(renderMarkdownHtml(content, {
|
|
26
|
+
imageBaseUrl: resolveRepositoryImage ? null : imageBaseUrl,
|
|
27
|
+
markdownFilePath,
|
|
28
|
+
deferRepositoryImages: Boolean(resolveRepositoryImage),
|
|
29
|
+
}))
|
|
13
30
|
|
|
14
|
-
function
|
|
15
|
-
if (!
|
|
31
|
+
async function resolveRepositoryImages(runId: number) {
|
|
32
|
+
if (!root || !markdownFilePath || !resolveRepositoryImage) return
|
|
16
33
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (
|
|
21
|
-
|
|
34
|
+
const images = Array.from(root.querySelectorAll('img[data-markdown-repository-path]'))
|
|
35
|
+
await Promise.all(images.map(async (image) => {
|
|
36
|
+
const repositoryPath = image.getAttribute('data-markdown-repository-path')
|
|
37
|
+
if (!repositoryPath) return
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const resolvedSrc = await resolveRepositoryImage(repositoryPath)
|
|
41
|
+
if (runId !== imageResolutionId || !resolvedSrc) return
|
|
42
|
+
image.setAttribute('src', resolvedSrc)
|
|
43
|
+
image.removeAttribute('data-markdown-repository-path')
|
|
44
|
+
} catch {
|
|
45
|
+
// Leave the image inert when an asset is missing or cannot be previewed.
|
|
46
|
+
}
|
|
47
|
+
}))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
$effect(() => {
|
|
51
|
+
const runId = ++imageResolutionId
|
|
52
|
+
void html
|
|
53
|
+
if (!root || !markdownFilePath || !resolveRepositoryImage) return
|
|
54
|
+
|
|
55
|
+
void resolveRepositoryImages(runId)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
onDestroy(() => {
|
|
59
|
+
imageResolutionId++
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
function handleClick(event: MouseEvent) {
|
|
63
|
+
if (!(event.target instanceof Element)) return
|
|
64
|
+
|
|
65
|
+
const anchor = event.target.closest('a')
|
|
66
|
+
const href = anchor?.getAttribute('href')
|
|
67
|
+
if (!anchor || !href) return
|
|
68
|
+
|
|
69
|
+
if (markdownFilePath) {
|
|
70
|
+
const repositoryPath = resolveMarkdownRepositoryPath(href, markdownFilePath)
|
|
71
|
+
if (repositoryPath) {
|
|
72
|
+
event.preventDefault()
|
|
73
|
+
void onOpenRepositoryPath?.(repositoryPath, getMarkdownRepositoryLinkSuffix(href))
|
|
74
|
+
return
|
|
22
75
|
}
|
|
23
76
|
}
|
|
77
|
+
|
|
78
|
+
if (href.startsWith('#')) return
|
|
79
|
+
|
|
80
|
+
event.preventDefault()
|
|
81
|
+
if (onOpenUrl && anchor.href) {
|
|
82
|
+
void onOpenUrl(href.startsWith('//') ? `https:${href}` : anchor.href)
|
|
83
|
+
}
|
|
24
84
|
}
|
|
25
85
|
</script>
|
|
26
86
|
|
|
27
87
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
28
|
-
<div role="presentation" class="markdown-body" onclick={handleClick}>
|
|
88
|
+
<div bind:this={root} role="presentation" class="markdown-body" onclick={handleClick}>
|
|
29
89
|
{@html html}
|
|
30
90
|
</div>
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onDestroy, tick } from 'svelte'
|
|
3
|
+
import type { Snippet } from 'svelte'
|
|
4
|
+
|
|
5
|
+
export type ModalInitialFocus = HTMLElement | string | (() => HTMLElement | null | undefined) | null | undefined
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
onClose: () => void
|
|
9
|
+
maxWidth?: string
|
|
10
|
+
overflowVisible?: boolean
|
|
11
|
+
initialFocus?: ModalInitialFocus
|
|
12
|
+
ariaLabel?: string
|
|
13
|
+
showHeader?: boolean
|
|
14
|
+
closeLabel?: string
|
|
15
|
+
closeDisabled?: boolean
|
|
16
|
+
onKeydown?: (event: KeyboardEvent) => boolean | void
|
|
17
|
+
testId?: string
|
|
18
|
+
modalClass?: string
|
|
19
|
+
boxClass?: string
|
|
20
|
+
header?: Snippet
|
|
21
|
+
children: Snippet
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let { onClose, maxWidth = '500px', overflowVisible = false, initialFocus, ariaLabel, showHeader = true, closeLabel = 'Close dialog', closeDisabled = false, onKeydown, testId, modalClass = '', boxClass = '', header, children }: Props = $props()
|
|
25
|
+
|
|
26
|
+
let modalElement: HTMLDivElement | null = $state(null)
|
|
27
|
+
let hasAppliedInitialFocus = false
|
|
28
|
+
const returnFocusTarget = typeof document !== 'undefined'
|
|
29
|
+
&& document.activeElement instanceof HTMLElement
|
|
30
|
+
&& document.activeElement !== document.body
|
|
31
|
+
? document.activeElement
|
|
32
|
+
: null
|
|
33
|
+
|
|
34
|
+
onDestroy(() => {
|
|
35
|
+
if (!returnFocusTarget?.isConnected) return
|
|
36
|
+
|
|
37
|
+
const activeElement = document.activeElement
|
|
38
|
+
const focusRemainedInModal = activeElement === document.body
|
|
39
|
+
|| (activeElement instanceof HTMLElement && !activeElement.isConnected)
|
|
40
|
+
|| (activeElement instanceof Node && modalElement?.contains(activeElement))
|
|
41
|
+
|
|
42
|
+
if (focusRemainedInModal) returnFocusTarget.focus()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
function resolveInitialFocusTarget(): HTMLElement | null {
|
|
46
|
+
if (!modalElement) return null
|
|
47
|
+
|
|
48
|
+
if (typeof initialFocus === 'function') {
|
|
49
|
+
return initialFocus() ?? modalElement
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (typeof initialFocus === 'string') {
|
|
53
|
+
return modalElement.querySelector<HTMLElement>(initialFocus) ?? modalElement
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return initialFocus ?? modalElement
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function focusInitialTarget() {
|
|
60
|
+
const target = resolveInitialFocusTarget()
|
|
61
|
+
target?.focus()
|
|
62
|
+
|
|
63
|
+
if (initialFocus !== undefined) {
|
|
64
|
+
void tick().then(() => {
|
|
65
|
+
const active = document.activeElement
|
|
66
|
+
if (active === document.body || active === modalElement || (active && !modalElement?.contains(active))) {
|
|
67
|
+
resolveInitialFocusTarget()?.focus()
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
$effect(() => {
|
|
74
|
+
if (!modalElement || hasAppliedInitialFocus) return
|
|
75
|
+
|
|
76
|
+
hasAppliedInitialFocus = true
|
|
77
|
+
void focusInitialTarget()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
function getFocusableElements(): HTMLElement[] {
|
|
81
|
+
if (!modalElement) return []
|
|
82
|
+
|
|
83
|
+
const selector = [
|
|
84
|
+
'a[href]',
|
|
85
|
+
'button:not([disabled])',
|
|
86
|
+
'textarea:not([disabled])',
|
|
87
|
+
'input:not([disabled])',
|
|
88
|
+
'select:not([disabled])',
|
|
89
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
90
|
+
].join(',')
|
|
91
|
+
|
|
92
|
+
return Array.from(modalElement.querySelectorAll<HTMLElement>(selector))
|
|
93
|
+
.filter((element) => !element.hasAttribute('disabled') && element.getAttribute('aria-hidden') !== 'true')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function keepFocusInsideModal(e: KeyboardEvent) {
|
|
97
|
+
const focusable = getFocusableElements()
|
|
98
|
+
if (focusable.length === 0) {
|
|
99
|
+
e.preventDefault()
|
|
100
|
+
modalElement?.focus()
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const first = focusable[0]
|
|
105
|
+
const last = focusable[focusable.length - 1]
|
|
106
|
+
const active = document.activeElement
|
|
107
|
+
|
|
108
|
+
if (e.shiftKey && (active === first || active === modalElement || !modalElement?.contains(active))) {
|
|
109
|
+
e.preventDefault()
|
|
110
|
+
last.focus()
|
|
111
|
+
} else if (!e.shiftKey && active === last) {
|
|
112
|
+
e.preventDefault()
|
|
113
|
+
first.focus()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
118
|
+
if (onKeydown?.(e)) {
|
|
119
|
+
e.stopPropagation()
|
|
120
|
+
return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (e.metaKey || e.ctrlKey || e.altKey) return
|
|
124
|
+
|
|
125
|
+
e.stopPropagation()
|
|
126
|
+
|
|
127
|
+
if (e.key === 'Escape') {
|
|
128
|
+
if (!closeDisabled) onClose()
|
|
129
|
+
} else if (e.key === 'Tab') {
|
|
130
|
+
keepFocusInsideModal(e)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function handleOverlayClick(e: MouseEvent) {
|
|
135
|
+
if (!closeDisabled && e.target === e.currentTarget) {
|
|
136
|
+
onClose()
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function handleCloseButtonClick() {
|
|
141
|
+
if (!closeDisabled) onClose()
|
|
142
|
+
}
|
|
143
|
+
</script>
|
|
144
|
+
|
|
145
|
+
<div bind:this={modalElement} class="modal modal-open {modalClass}" data-testid={testId} onclick={handleOverlayClick} onkeydown={handleKeydown} role="dialog" aria-modal="true" aria-label={ariaLabel} tabindex="-1">
|
|
146
|
+
<div class="modal-box bg-base-100 shadow-xl p-0 flex flex-col max-h-[90vh] {overflowVisible ? 'overflow-visible' : ''} {boxClass}" style="max-width: {maxWidth}">
|
|
147
|
+
{#if showHeader}
|
|
148
|
+
<div class="flex items-center justify-between px-5 py-4 border-b border-base-300">
|
|
149
|
+
{#if header}
|
|
150
|
+
{@render header()}
|
|
151
|
+
{/if}
|
|
152
|
+
<button class="btn btn-ghost btn-xs shrink-0" aria-label={closeLabel} onclick={handleCloseButtonClick} type="button" disabled={closeDisabled}>✕</button>
|
|
153
|
+
</div>
|
|
154
|
+
{/if}
|
|
155
|
+
{@render children()}
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
title: string
|
|
6
|
+
subtitle?: string | null
|
|
7
|
+
surface?: 'default' | 'subtle'
|
|
8
|
+
actions?: Snippet
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
let { title, subtitle = null, surface = 'subtle', actions }: Props = $props()
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<header class="flex items-center justify-between gap-4 border-b border-base-300 px-6 py-3 shrink-0 {surface === 'default' ? 'bg-base-100' : 'bg-base-200'}">
|
|
15
|
+
<div class="min-w-0">
|
|
16
|
+
<h2 class="text-[22px] font-semibold text-base-content tracking-tight m-0 truncate">{title}</h2>
|
|
17
|
+
{#if subtitle}
|
|
18
|
+
<p class="text-[13px] text-secondary mt-0.5 m-0 truncate">{subtitle}</p>
|
|
19
|
+
{/if}
|
|
20
|
+
</div>
|
|
21
|
+
{#if actions}
|
|
22
|
+
<div class="shrink-0">
|
|
23
|
+
{@render actions()}
|
|
24
|
+
</div>
|
|
25
|
+
{/if}
|
|
26
|
+
</header>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
accessibleName: string
|
|
6
|
+
active: boolean
|
|
7
|
+
collapsed: boolean
|
|
8
|
+
onActivate: () => void
|
|
9
|
+
leading?: Snippet
|
|
10
|
+
label?: Snippet
|
|
11
|
+
trailing?: Snippet
|
|
12
|
+
class?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
accessibleName,
|
|
17
|
+
active,
|
|
18
|
+
collapsed,
|
|
19
|
+
onActivate,
|
|
20
|
+
leading,
|
|
21
|
+
label,
|
|
22
|
+
trailing,
|
|
23
|
+
class: className,
|
|
24
|
+
}: Props = $props()
|
|
25
|
+
|
|
26
|
+
function handleKeydown(event: KeyboardEvent): void {
|
|
27
|
+
if (event.key !== 'Enter' && event.key !== ' ') return
|
|
28
|
+
event.preventDefault()
|
|
29
|
+
onActivate()
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
class={[
|
|
36
|
+
'relative mx-2 flex min-h-11 w-auto items-center rounded-lg gap-3 py-2.5 cursor-pointer transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary',
|
|
37
|
+
collapsed ? 'justify-center px-0' : 'px-3',
|
|
38
|
+
active ? 'bg-primary/10 text-primary' : 'text-base-content/55 hover:bg-base-200 hover:text-base-content',
|
|
39
|
+
className,
|
|
40
|
+
]}
|
|
41
|
+
title={collapsed ? accessibleName : undefined}
|
|
42
|
+
aria-label={accessibleName}
|
|
43
|
+
aria-current={active ? 'page' : undefined}
|
|
44
|
+
onclick={onActivate}
|
|
45
|
+
onkeydown={handleKeydown}
|
|
46
|
+
>
|
|
47
|
+
{#if leading}
|
|
48
|
+
<span class="relative shrink-0">{@render leading()}</span>
|
|
49
|
+
{/if}
|
|
50
|
+
{#if !collapsed}
|
|
51
|
+
{#if label}<span class="min-w-0 text-sm font-medium">{@render label()}</span>{/if}
|
|
52
|
+
{#if trailing}<span class="ml-auto shrink-0">{@render trailing()}</span>{/if}
|
|
53
|
+
{/if}
|
|
54
|
+
</button>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
loading?: boolean
|
|
6
|
+
loadingLabel?: string
|
|
7
|
+
error?: string | null
|
|
8
|
+
errorTitle?: string
|
|
9
|
+
empty?: boolean
|
|
10
|
+
emptyTitle?: string
|
|
11
|
+
emptyDescription?: string | null
|
|
12
|
+
retryLabel?: string
|
|
13
|
+
retryDisabled?: boolean
|
|
14
|
+
onRetry?: () => void
|
|
15
|
+
errorActions?: Snippet
|
|
16
|
+
emptyActions?: Snippet
|
|
17
|
+
children?: Snippet
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
loading = false,
|
|
22
|
+
loadingLabel = 'Loading…',
|
|
23
|
+
error = null,
|
|
24
|
+
errorTitle = 'Unable to load',
|
|
25
|
+
empty = false,
|
|
26
|
+
emptyTitle = 'Nothing to show',
|
|
27
|
+
emptyDescription = null,
|
|
28
|
+
retryLabel = 'Retry',
|
|
29
|
+
retryDisabled = false,
|
|
30
|
+
onRetry,
|
|
31
|
+
errorActions,
|
|
32
|
+
emptyActions,
|
|
33
|
+
children,
|
|
34
|
+
}: Props = $props()
|
|
35
|
+
|
|
36
|
+
function handleRetry() {
|
|
37
|
+
if (retryDisabled) return
|
|
38
|
+
onRetry?.()
|
|
39
|
+
}
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
{#if loading}
|
|
43
|
+
<div class="flex h-full min-h-0 flex-1 flex-col items-center justify-center gap-3 p-6 text-center text-sm text-base-content/70" role="status" aria-live="polite">
|
|
44
|
+
<span class="loading loading-spinner loading-md text-primary" aria-label={loadingLabel}></span>
|
|
45
|
+
<span>{loadingLabel}</span>
|
|
46
|
+
</div>
|
|
47
|
+
{:else if error !== null}
|
|
48
|
+
<div class="flex h-full min-h-0 flex-1 flex-col items-center justify-center gap-3 p-6 text-center text-sm text-base-content/70" role="alert" aria-live="assertive">
|
|
49
|
+
<div class="badge badge-error badge-lg">Issue</div>
|
|
50
|
+
<h3 class="m-0 text-xl font-semibold text-base-content">{errorTitle}</h3>
|
|
51
|
+
{#if error}
|
|
52
|
+
<p class="m-0 max-w-md break-words text-base-content/70">{error}</p>
|
|
53
|
+
{/if}
|
|
54
|
+
{#if errorActions}
|
|
55
|
+
<div class="flex flex-wrap items-center justify-center gap-2 pt-1">
|
|
56
|
+
{@render errorActions()}
|
|
57
|
+
</div>
|
|
58
|
+
{:else if onRetry}
|
|
59
|
+
<button class="btn btn-primary btn-sm" type="button" onclick={handleRetry} disabled={retryDisabled}>{retryLabel}</button>
|
|
60
|
+
{/if}
|
|
61
|
+
</div>
|
|
62
|
+
{:else if empty}
|
|
63
|
+
<div class="flex h-full min-h-0 flex-1 flex-col items-center justify-center gap-3 p-6 text-center text-sm text-base-content/70" role="status" aria-live="polite">
|
|
64
|
+
<h3 class="m-0 text-xl font-semibold text-base-content">{emptyTitle}</h3>
|
|
65
|
+
{#if emptyDescription}
|
|
66
|
+
<p class="m-0 max-w-md text-base-content/70">{emptyDescription}</p>
|
|
67
|
+
{/if}
|
|
68
|
+
{#if emptyActions}
|
|
69
|
+
<div class="flex flex-wrap items-center justify-center gap-2 pt-1">
|
|
70
|
+
{@render emptyActions()}
|
|
71
|
+
</div>
|
|
72
|
+
{/if}
|
|
73
|
+
</div>
|
|
74
|
+
{:else}
|
|
75
|
+
{@render children?.()}
|
|
76
|
+
{/if}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
minWidth?: number
|
|
9
9
|
maxWidth?: number
|
|
10
10
|
side?: 'left' | 'right'
|
|
11
|
+
label?: string
|
|
11
12
|
children?: Snippet
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
minWidth = 120,
|
|
18
19
|
maxWidth = 600,
|
|
19
20
|
side = 'left',
|
|
21
|
+
label = side === 'left' ? 'left' : 'right',
|
|
20
22
|
children,
|
|
21
23
|
}: Props = $props()
|
|
22
24
|
|
|
@@ -119,6 +121,10 @@
|
|
|
119
121
|
style="cursor: col-resize"
|
|
120
122
|
role="separator"
|
|
121
123
|
aria-orientation="vertical"
|
|
124
|
+
aria-label="Resize {label} panel"
|
|
125
|
+
aria-valuemin={minWidth}
|
|
126
|
+
aria-valuemax={maxWidth}
|
|
127
|
+
aria-valuenow={width}
|
|
122
128
|
tabindex="0"
|
|
123
129
|
onmousedown={onMouseDown}
|
|
124
130
|
ondblclick={onDblClick}
|
|
@@ -137,6 +143,10 @@
|
|
|
137
143
|
style="cursor: col-resize"
|
|
138
144
|
role="separator"
|
|
139
145
|
aria-orientation="vertical"
|
|
146
|
+
aria-label="Resize {label} panel"
|
|
147
|
+
aria-valuemin={minWidth}
|
|
148
|
+
aria-valuemax={maxWidth}
|
|
149
|
+
aria-valuenow={width}
|
|
140
150
|
tabindex="0"
|
|
141
151
|
onmousedown={onMouseDown}
|
|
142
152
|
ondblclick={onDblClick}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Vendored file-type icons
|
|
2
|
+
|
|
3
|
+
These SVGs are copied from the MIT-licensed `vscode-material-icons` package
|
|
4
|
+
(Material Icon Theme). Do not edit by hand.
|
|
5
|
+
|
|
6
|
+
Regenerate after changing the icon maps / `BUNDLED_ICON_NAMES` in
|
|
7
|
+
`../../fileIcons.ts` (and keep the `NAMES` list in the generator in sync):
|
|
8
|
+
|
|
9
|
+
pnpm --filter @openforge-app/plugin-sdk run icons:generate
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M19.56348,22A5.56981,5.56981,0,0,1,14,16.43652v-2.873A5.56977,5.56977,0,0,1,19.56348,8H24V2H19.56348A11.56349,11.56349,0,0,0,8,13.56348v2.873A11.56354,11.56354,0,0,0,19.56348,28H24V22Z" style="fill:#0288d1"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M20 19V7H4v12h16m0-16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16m-7 14v-2h5v2h-5m-3.42-4L5.57 9H8.4l3.3 3.3c.39.39.39 1.03 0 1.42L8.42 17H5.59z" fill="#ff7043"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><polygon points="28 14 28 10 26 10 26 14 20 14 20 10 18 10 18 14 14 14 14 16 18 16 18 20 20 20 20 16 26 16 26 20 28 20 28 16 32 16 32 14 28 14" style="fill:#0288d1"/><path d="M13.56348,22A5.56981,5.56981,0,0,1,8,16.43652v-2.873A5.56977,5.56977,0,0,1,13.56348,8H18V2H13.56348A11.56349,11.56349,0,0,0,2,13.56348v2.873A11.56354,11.56354,0,0,0,13.56348,28H18V22Z" style="fill:#0288d1"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30,14V12H28V8H26v4H24V8H22v4H20v2h2v2H20v2h2v4h2V18h2v4h2V18h2V16H28V14Zm-4,2H24V14h2Z" style="fill:#0288d1"/><path d="M13.56348,22A5.56981,5.56981,0,0,1,8,16.43652v-2.873A5.56977,5.56977,0,0,1,13.56348,8H18V2H13.56348A11.56349,11.56349,0,0,0,2,13.56348v2.873A11.56354,11.56354,0,0,0,13.56348,28H18V22Z" style="fill:#0288d1"/></svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<polygon points="29.18 4 25.61 22.36 25.28 24 20.54 25.57 17.26 26.66 13.21 28 2.87 24.05 4.05 18 8.25 18 7.81 20.85 14.15 23.27 14.93 23.01 21.45 20.85 21.62 20.02 22.41 16 4.44 16 5.18 12.24 5.23 12 23.19 12 23.97 8 6 8 6.78 4 29.18 4" style="fill: #42a5f5"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M16,24c-5.525,0-10-.9-10-2v4c0,1.1,4.475,2,10,2s10-.9,10-2V22C26,23.1,21.525,24,16,24Z" style="fill:#ffca28"/><path d="M16,16c-5.525,0-10-.9-10-2v4c0,1.1,4.475,2,10,2s10-.9,10-2V14C26,15.1,21.525,16,16,16Z" style="fill:#ffca28"/><path d="M16,4C10.47717,4,6,4.89539,6,6v4c0,1.1,4.475,2,10,2s10-.9,10-2V6C26,4.89539,21.52283,4,16,4Z" style="fill:#ffca28"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg version="1.1" viewBox="0 0 24 24" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="m21.81 10.25c-0.06-0.04-0.56-0.43-1.64-0.43-0.28 0-0.56 0.03-0.84 0.08-0.21-1.4-1.38-2.11-1.43-2.14l-0.29-0.17-0.18 0.27c-0.24 0.36-0.43 0.77-0.51 1.19-0.2 0.8-0.08 1.56 0.33 2.21-0.49 0.28-1.29 0.35-1.46 0.35h-13.17c-0.34 0-0.62 0.28-0.62 0.63 0 1.15 0.18 2.3 0.58 3.38 0.45 1.19 1.13 2.07 2 2.61 0.98 0.6 2.59 0.94 4.42 0.94 0.79 0 1.61-0.07 2.42-0.22 1.12-0.2 2.2-0.59 3.19-1.16 0.82-0.47 1.55-1.07 2.17-1.79 1.05-1.17 1.67-2.5 2.12-3.65h0.19c1.14 0 1.85-0.46 2.24-0.85 0.26-0.24 0.45-0.53 0.59-0.87l0.08-0.24-0.19-0.14m-17.96 0.99h1.76c0.08 0 0.16-0.07 0.16-0.16v-1.58c0-0.08-0.07-0.16-0.16-0.16h-1.76c-0.09 0-0.16 0.07-0.16 0.16v1.58c0.01 0.09 0.07 0.16 0.16 0.16m2.43 0h1.76c0.08 0 0.16-0.07 0.16-0.16v-1.58c0-0.08-0.07-0.16-0.16-0.16h-1.76c-0.09 0-0.16 0.07-0.16 0.16v1.58c0.01 0.09 0.07 0.16 0.16 0.16m2.47 0h1.75c0.1 0 0.17-0.07 0.17-0.16v-1.58c0-0.08-0.06-0.16-0.17-0.16h-1.75c-0.08 0-0.15 0.07-0.15 0.16v1.58c0 0.09 0.06 0.16 0.15 0.16m2.44 0h1.77c0.08 0 0.15-0.07 0.15-0.16v-1.58c0-0.08-0.06-0.16-0.15-0.16h-1.77c-0.08 0-0.15 0.07-0.15 0.16v1.58c0 0.09 0.07 0.16 0.15 0.16m-4.91-2.24h1.76c0.08 0 0.16-0.09 0.16-0.18v-1.57c0-0.09-0.07-0.16-0.16-0.16h-1.76c-0.09 0-0.16 0.06-0.16 0.16v1.57c0.01 0.09 0.07 0.18 0.16 0.18m2.47 0h1.75c0.1 0 0.17-0.09 0.17-0.18v-1.57c0-0.09-0.06-0.16-0.17-0.16h-1.75c-0.08 0-0.15 0.06-0.15 0.16v1.57c0 0.09 0.06 0.18 0.15 0.18m2.44 0h1.77c0.08 0 0.15-0.09 0.15-0.18v-1.57c0-0.09-0.07-0.16-0.15-0.16h-1.77c-0.08 0-0.15 0.06-0.15 0.16v1.57c0 0.09 0.07 0.18 0.15 0.18m0-2.28h1.77c0.08 0 0.15-0.07 0.15-0.16v-1.56c0-0.1-0.07-0.17-0.15-0.17h-1.77c-0.08 0-0.15 0.06-0.15 0.17v1.56c0 0.08 0.07 0.16 0.15 0.16m2.46 4.52h1.76c0.09 0 0.16-0.07 0.16-0.16v-1.58c0-0.08-0.07-0.16-0.16-0.16h-1.76c-0.08 0-0.15 0.07-0.15 0.16v1.58c0 0.09 0.07 0.16 0.15 0.16" fill="#0087c9"/></svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="24px" height="24px" version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M0 0h24v24H0V0z" fill="none"/>
|
|
3
|
+
<path d="M8 16h8v2H8zm0-4h8v2H8zm6-10H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z" fill="#42a5f5"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M13 9h5.5L13 3.5V9M6 2h8l6 6v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4c0-1.11.89-2 2-2m5 2H6v16h12v-9h-7V4z" fill="#90a4ae" /></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M28.96692,12H9.44152a2,2,0,0,0-1.89737,1.36754L4,24V10H28a2,2,0,0,0-2-2H15.1241a2,2,0,0,1-1.28038-.46357L12.5563,6.46357A2,2,0,0,0,11.27592,6H4A2,2,0,0,0,2,8V24a2,2,0,0,0,2,2H26l4.80523-11.21213A2,2,0,0,0,28.96692,12Z" fill="#90a4ae" /></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M13.84376,7.53645l-1.28749-1.0729A2,2,0,0,0,11.27591,6H4A2,2,0,0,0,2,8V24a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2H15.12412A2,2,0,0,1,13.84376,7.53645Z" fill="#90a4ae" /></svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<path d="M13.17157,2.82843,11.78,4.22l1.91,1.91,2,2A2.98634,2.98634,0,0,1,20,10.81a3.24762,3.24762,0,0,1-.31,1.31l2.06,2a2.68305,2.68305,0,0,1,3.37.57A2.86047,2.86047,0,0,1,26,16.80725a3.01491,3.01491,0,0,1-.856,2.10855A2.89268,2.89268,0,0,1,23,19.81a2.92912,2.92912,0,0,1-2.13-.87,2.69441,2.69441,0,0,1-.56-3.38l-2-2.06a2.54662,2.54662,0,0,1-.31.12V20a3.02384,3.02384,0,0,1,1.44,1.09A2.92105,2.92105,0,0,1,20,22.81a2.88288,2.88288,0,0,1-.878,2.128,2.98224,2.98224,0,0,1-2.04815.87125A2.98125,2.98125,0,0,1,14.56,21.09,3.02384,3.02384,0,0,1,16,20V13.62a2.9626,2.9626,0,0,1-1.44-1.09A2.89859,2.89859,0,0,1,14,10.81a2.86805,2.86805,0,0,1,.31-1.31l-3.9-3.9L2.83113,13.17157a4,4,0,0,0-.00135,5.65821L13.17157,29.17157a4,4,0,0,0,5.65686,0L29.17157,18.82843a4,4,0,0,0,0-5.65686L18.82843,2.82843A4,4,0,0,0,13.17157,2.82843Z" style="fill: #e64a19"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect x="2" y="12" width="4" height="2" style="fill:#00acc1"/><rect y="16" width="6" height="2" style="fill:#00acc1"/><rect x="4" y="20" width="2" height="2" style="fill:#00acc1"/><path d="M20.9541,15H14v3H17.239a4.41613,4.41613,0,0,1-3.531,2,2.64933,2.64933,0,0,1-2.05329-.85828,2.85507,2.85507,0,0,1-.62756-2.27887A4.51451,4.51451,0,0,1,15.29205,13a2.72468,2.72468,0,0,1,1.74872.58368L20.0025,12.399A5.60478,5.60478,0,0,0,15.29205,10a7.52569,7.52569,0,0,0-7.24292,6.5A5.61427,5.61427,0,0,0,13.708,23a7.52569,7.52569,0,0,0,7.24292-6.5A6.40392,6.40392,0,0,0,20.9541,15Z" style="fill:#00acc1"/><path d="M26.29205,10a7.52569,7.52569,0,0,0-7.24292,6.5A5.61427,5.61427,0,0,0,24.708,23a7.5258,7.5258,0,0,0,7.243-6.5A5.61428,5.61428,0,0,0,26.29205,10ZM28.973,16.13708A4.51463,4.51463,0,0,1,24.708,20a2.64906,2.64906,0,0,1-2.05323-.85822,2.85508,2.85508,0,0,1-.62762-2.27893A4.51451,4.51451,0,0,1,26.29205,13a2.64919,2.64919,0,0,1,2.05329.85822A2.855,2.855,0,0,1,28.973,16.13708Z" style="fill:#00acc1"/></svg>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<rect x="6" y="20" width="20" height="2" style="fill: #ec407a"/>
|
|
3
|
+
<circle cx="7" cy="21" r="3" style="fill: #ec407a"/>
|
|
4
|
+
<circle cx="16" cy="27" r="3" style="fill: #ec407a"/>
|
|
5
|
+
<circle cx="25" cy="21" r="3" style="fill: #ec407a"/>
|
|
6
|
+
<rect x="6" y="10" width="20" height="2" style="fill: #ec407a"/>
|
|
7
|
+
<circle cx="7" cy="11" r="3" style="fill: #ec407a"/>
|
|
8
|
+
<circle cx="16" cy="5" r="3" style="fill: #ec407a"/>
|
|
9
|
+
<circle cx="25" cy="11" r="3" style="fill: #ec407a"/>
|
|
10
|
+
<rect x="6" y="12" width="2" height="10" style="fill: #ec407a"/>
|
|
11
|
+
<rect x="24" y="10" width="2" height="12" style="fill: #ec407a"/>
|
|
12
|
+
<rect x="3.57231" y="22.70513" width="13.54352" height="2" transform="translate(13.44481 -1.97206) rotate(30.46034)" style="fill: #ec407a"/>
|
|
13
|
+
<rect x="13.57231" y="23.29487" width="13.54352" height="2" transform="translate(50.19633 34.92331) rotate(149.53966)" style="fill: #ec407a"/>
|
|
14
|
+
<rect x="4.59753" y="6.57997" width="13.54352" height="2" transform="translate(-2.29249 7.3106) rotate(-32.56204)" style="fill: #ec407a"/>
|
|
15
|
+
<rect x="13.85895" y="6.57997" width="13.54352" height="2" transform="translate(33.93883 25.07214) rotate(-147.43796)" style="fill: #ec407a"/>
|
|
16
|
+
<rect x="4.59753" y="23.42003" width="13.54352" height="2" transform="translate(14.93032 -2.28049) rotate(32.56204)" style="fill: #ec407a"/>
|
|
17
|
+
<rect x="13.85895" y="23.42003" width="13.54352" height="2" transform="translate(51.16163 33.89775) rotate(147.43796)" style="fill: #ec407a"/>
|
|
18
|
+
<rect x="1.10394" y="12.6161" width="21.47138" height="2" transform="translate(-5.75891 17.59934) rotate(-61.7454)" style="fill: #ec407a"/>
|
|
19
|
+
<rect x="9.42467" y="12.6161" width="21.47138" height="2" transform="translate(17.71031 37.82014) rotate(-118.2546)" style="fill: #ec407a"/>
|
|
20
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="m13 9h5.5l-5.5-5.5v5.5m-7-7h8l6 6v12a2 2 0 0 1-2 2h-12c-1.11 0-2-0.9-2-2v-16c0-1.11 0.89-2 2-2m0 18h9 3v-8l-4 4-2-2-6 6m2-11a2 2 0 0 0-2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0-2-2z" fill="#26a69f"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<g>
|
|
3
|
+
<rect x="4" y="26" width="24" height="2" style="fill: #f44336"/>
|
|
4
|
+
<path d="M28,4H7A1,1,0,0,0,6,5V18a4,4,0,0,0,4,4H20a4,4,0,0,0,4-4V14h4a2,2,0,0,0,2-2V6A2,2,0,0,0,28,4Zm0,8H24V6h4Z" style="fill: #f44336"/>
|
|
5
|
+
</g>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="m2 2h12v12h-12v-12m3.1533 10.027c0.26667 0.56667 0.79333 1.0333 1.6933 1.0333 1 0 1.6867-0.53333 1.6867-1.7v-3.8533h-1.1333v3.8267c0 0.57333-0.23333 0.72-0.6 0.72-0.38667 0-0.54667-0.26667-0.72667-0.58l-0.92 0.55333m3.9867-0.12c0.33333 0.65333 1.0067 1.1533 2.06 1.1533 1.0667 0 1.8667-0.55333 1.8667-1.5733 0-0.94-0.54-1.36-1.5-1.7733l-0.28-0.12c-0.48667-0.20667-0.69333-0.34667-0.69333-0.68 0-0.27333 0.20667-0.48667 0.54-0.48667 0.32 0 0.53333 0.14 0.72667 0.48667l0.87333-0.58c-0.36667-0.64-0.88667-0.88667-1.6-0.88667-1.0067 0-1.6533 0.64-1.6533 1.4867 0 0.92 0.54 1.3533 1.3533 1.7l0.28 0.12c0.52 0.22667 0.82667 0.36667 0.82667 0.75333 0 0.32-0.3 0.55333-0.76667 0.55333-0.55333 0-0.87333-0.28667-1.1133-0.68667z" fill="#ffca28" stroke-width=".66667"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="24" height="24" version="1.1" viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M560-160v-80h120q17 0 28.5-11.5T720-280v-80q0-38 22-69t58-44v-14q-36-13-58-44t-22-69v-80q0-17-11.5-28.5T680-720H560v-80h120q50 0 85 35t35 85v80q0 17 11.5 28.5T840-560h40v160h-40q-17 0-28.5 11.5T800-360v80q0 50-35 85t-85 35H560Zm-280 0q-50 0-85-35t-35-85v-80q0-17-11.5-28.5T120-400H80v-160h40q17 0 28.5-11.5T160-600v-80q0-50 35-85t85-35h120v80H280q-17 0-28.5 11.5T240-680v80q0 38-22 69t-58 44v14q36 13 58 44t22 69v80q0 17 11.5 28.5T280-240h120v80H280Z" fill="#f9a825"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="a" x1="1.725" x2="22.185" y1="22.67" y2="1.982" gradientTransform="translate(1.306 1.129) scale(.89324)" gradientUnits="userSpaceOnUse"><stop stop-color="#0296d8" offset="0"/><stop stop-color="#8371d9" offset="1"/></linearGradient><linearGradient id="b" x1="1.869" x2="22.798" y1="22.382" y2="3.377" gradientTransform="translate(1.323 1.129) scale(.89324)" gradientUnits="userSpaceOnUse"><stop stop-color="#cb55c0" offset="0"/><stop stop-color="#f28e0e" offset="1"/></linearGradient></defs><path d="M2.975 2.976v18.048h18.05v-.03l-4.478-4.511-4.48-4.515 4.48-4.515 4.443-4.477z" fill="url(#a)"/><path d="m12.223 2.976-9.23 9.23v8.818h.083l9.032-9.032-.024-.024 4.48-4.515 4.443-4.477h-8.784z" fill="url(#b)"/></svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<g>
|
|
3
|
+
<polygon points="14 10 10 13.5 6 10 4 10 4 22 8 22 8 16 10 18 12 16 12 22 16 22 16 10 14 10" style="fill: #42a5f5"/>
|
|
4
|
+
<polygon points="26 16 26 10 22 10 22 16 18 16 24 24 30 16 26 16" style="fill: #42a5f5"/>
|
|
5
|
+
</g>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<g>
|
|
3
|
+
<path d="M16,20.00258v2h4a2,2,0,0,0,2-2v-2a2,2,0,0,0-2-2H18v-2h4v-2H18a2,2,0,0,0-2,2v2a2,2,0,0,0,2,2h2v2Z" style="fill: #8bc34a"/>
|
|
4
|
+
<path d="M16,3.00258l-12,7v14l4,2h6v-13.5a.5.5,0,0,0-.5-.5h-1a.5.5,0,0,0-.5.5v11.5H7.99986L6,22.96875V11.15132L16,5.318l10,5.83331V22.85384L16,28.68715l-1.74542-1.022L13,29.25258l3,1.75,12-7v-14Z" style="fill: #8bc34a"/>
|
|
5
|
+
</g>
|
|
6
|
+
</svg>
|