Skip to content

Commit

Permalink
Implement UI for developer tab
Browse files Browse the repository at this point in the history
  • Loading branch information
HuakunShen committed May 13, 2024
1 parent e04ed98 commit 3f10d67
Show file tree
Hide file tree
Showing 33 changed files with 776 additions and 15 deletions.
178 changes: 178 additions & 0 deletions apps/desktop/components/settings/Developer/Installation.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<script setup lang="ts">
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { FolderCogIcon, AlertCircle, InfoIcon, ExternalLinkIcon } from "lucide-vue-next";
import {
AlertDialog,
AlertDialogAction,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { open } from "@tauri-apps/api/dialog";
import type { HTMLAttributes } from "vue";
import { cn } from "~/lib/utils";
const props = defineProps<{
class?: HTMLAttributes["class"];
}>();
async function pickProject() {
const selected = await open({
directory: true,
multiple: false,
});
console.log(selected);
}
</script>

<template>
<div :class="cn('h-full', props.class)">
<div class="container grid w-full max-w-2xl items-center gap-1.5">
<Alert variant="destructive" class="dark:text-red-600 dark:border-red-600">
<AlertCircle class="w-4 h-4" />
<AlertTitle>Warning: Powerful Extensions, Potential Security Risks</AlertTitle>
<AlertDescription>
<AlertDialog>
<AlertDialogTrigger as-child>
<button class="float-right">
<InfoIcon class="inline w-5 h-5" />
</button>
</AlertDialogTrigger>
<AlertDialogContent class="sm:max-w-[40em] overflow-auto max-h-[30em]">
<AlertDialogHeader>
<AlertDialogTitle class="text-red-600"
>Warning: Powerful Extensions, Potential Security Risks</AlertDialogTitle
>
<AlertDialogDescription>Be Careful Installing Any Extension Here</AlertDialogDescription>
</AlertDialogHeader>
<article class="prose dark:prose-invert">
<p>
This app allows you to install extensions from external sources. While these extensions can provide
advanced features and improve your experience (UX) and developer experience (DX), they also carry
significant security risks.
</p>

<h3>Understanding the Risks:</h3>
<ul>
<li>
<strong>File System and System API Access:</strong> Extensions can access your files and system
APIs, making them powerful but potentially dangerous.
</li>
<li>
<strong>Limited Review Process:</strong> Extensions from the app store can be reviewed by public,
but there's still a chance of encountering security vulnerabilities.
</li>
</ul>

<h3>Developer Settings and Untrusted Sources:</h3>
<ul>
<li>
<strong>Developer settings</strong> allow installing extensions from anywhere, like your local
machine or remote URLs. This is intended only for development purposes or when you absolutely trust
the source.
</li>
<li>
<strong>Loading extensions from remote URLs</strong> grants them access to your system, similar to
giving a website that level of control. Since the author can update the code at any time, malicious
extensions could steal information or take control of your system.
<strong>Remote URLs should only be used for development.</strong>
</li>
</ul>

<h3>Stay Safe: Trust Matters</h3>
<p>
Only install extensions from reputable sources that you trust (e.g. extensions written by yourself or
your organization). By being cautious, you can maximize the benefits and minimize the risks associated
with external extensions.
</p>
</article>
<AlertDialogFooter>
<AlertDialogAction>Understood</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</AlertDescription>
</Alert>

<p class="text-xs">
There are 3 options to install an extension in developer mode. Either load it from your local folder, download
from URL or load from a remote URL directly.
</p>
<Label class="text-xl" for="pick">Pick Project Folder to Install</Label>
<div class="flex justify-center">
<Card class="w-96 h-36">
<div class="flex justify-center items-center h-full cursor-pointer" @click="pickProject">
<div class="flex flex-col items-center">
<FolderCogIcon class="w-10 h-10" />
<small class="text-xs select-none">Click or Drag and Drop</small>
</div>
</div>
</Card>
</div>

<Label for="url" class="text-xl">
Download URL
<Popover>
<PopoverTrigger as-child>
<button class="-translate-y-0.5"><InfoIcon class="inline w-4 cursor-pointer" /></button>
</PopoverTrigger>
<PopoverContent class="w-96">
<TauriLink class="block" href="https://jarvis.huakun.tech/design/extensions/installation/">
Read Docs For More Details <ExternalLinkIcon class="inline w-4 -translate-y-0.5" />
</TauriLink>
<div class="text-xs">
It can be an npm package URL like
<TauriLink class="block" href="https://registry.npmjs.org/nest-neo4j/latest"
>https://registry.npmjs.org/nest-neo4j/latest</TauriLink
>
or a tarball url like
<TauriLink class="block" href="https://registry.npmjs.org/nest-neo4j/-/nest-neo4j-0.3.1.tgz"
>https://registry.npmjs.org/nest-neo4j/-/nest-neo4j-0.3.1.tgz</TauriLink
>
</div>
</PopoverContent>
</Popover>
</Label>
<div class="flex w-full items-center gap-1.5">
<Input id="url" type="text" placeholder="Download URL" />
<Button type="submit">Download</Button>
</div>

<Label for="url" class="text-xl">
Remote URL
<Popover>
<PopoverTrigger as-child>
<button class="-translate-y-0.5"><InfoIcon class="inline w-4 cursor-pointer" /></button>
</PopoverTrigger>
<PopoverContent class="w-96">
<TauriLink class="block" href="https://jarvis.huakun.tech/design/extensions/installation/">
Read Docs For More Details <ExternalLinkIcon class="inline w-4 -translate-y-0.5" />
</TauriLink>
<div class="text-xs">
It can be an npm package URL like
<TauriLink class="block" href="https://registry.npmjs.org/nest-neo4j/latest"
>https://registry.npmjs.org/nest-neo4j/latest</TauriLink
>
or a tarball url like
<TauriLink class="block" href="https://registry.npmjs.org/nest-neo4j/-/nest-neo4j-0.3.1.tgz"
>https://registry.npmjs.org/nest-neo4j/-/nest-neo4j-0.3.1.tgz</TauriLink
>
</div>
</PopoverContent>
</Popover>
</Label>
<div class="flex w-full items-center gap-1.5">
<Input id="url" type="text" placeholder="Remote URL" />
<Button type="submit">Install</Button>
</div>
</div>
</div>
</template>
22 changes: 20 additions & 2 deletions apps/desktop/components/settings/DeveloperTab.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
<script setup lang="ts">
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
const tags = Array.from({ length: 50 }).map((_, i, a) => `v1.2.0-beta.${a.length - i}`);
</script>

<template>

</template>
<div class="py-2 max-h-full overflow-auto flex">
<ScrollArea class="max-h-full w-48 rounded-md border">
<div class="p-4">
<h4 class="mb-4 text-sm font-medium leading-none">Tags</h4>

<div v-for="tag in tags" :key="tag">
<div class="text-sm">
{{ tag }}
</div>
<Separator class="my-2" />
</div>
</div>
</ScrollArea>
<SettingsDeveloperInstallation class="grow" />
</div>
</template>
14 changes: 14 additions & 0 deletions apps/desktop/components/ui/alert-dialog/AlertDialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts">
import { type AlertDialogEmits, type AlertDialogProps, AlertDialogRoot, useForwardPropsEmits } from 'radix-vue'
const props = defineProps<AlertDialogProps>()
const emits = defineEmits<AlertDialogEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>

<template>
<AlertDialogRoot v-bind="forwarded">
<slot />
</AlertDialogRoot>
</template>
20 changes: 20 additions & 0 deletions apps/desktop/components/ui/alert-dialog/AlertDialogAction.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AlertDialogAction, type AlertDialogActionProps } from 'radix-vue'
import { cn } from '@/lib/utils'
import { buttonVariants } from '@/components/ui/button'
const props = defineProps<AlertDialogActionProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>

<template>
<AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)">
<slot />
</AlertDialogAction>
</template>
20 changes: 20 additions & 0 deletions apps/desktop/components/ui/alert-dialog/AlertDialogCancel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AlertDialogCancel, type AlertDialogCancelProps } from 'radix-vue'
import { cn } from '@/lib/utils'
import { buttonVariants } from '@/components/ui/button'
const props = defineProps<AlertDialogCancelProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>

<template>
<AlertDialogCancel v-bind="delegatedProps" :class="cn(buttonVariants({ variant: 'outline' }), 'mt-2 sm:mt-0', props.class)">
<slot />
</AlertDialogCancel>
</template>
42 changes: 42 additions & 0 deletions apps/desktop/components/ui/alert-dialog/AlertDialogContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import {
AlertDialogContent,
type AlertDialogContentEmits,
type AlertDialogContentProps,
AlertDialogOverlay,
AlertDialogPortal,
useForwardPropsEmits,
} from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<AlertDialogContentProps & { class?: HTMLAttributes['class'] }>()
const emits = defineEmits<AlertDialogContentEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>

<template>
<AlertDialogPortal>
<AlertDialogOverlay
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
/>
<AlertDialogContent
v-bind="forwarded"
:class="
cn(
'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
props.class,
)
"
>
<slot />
</AlertDialogContent>
</AlertDialogPortal>
</template>
25 changes: 25 additions & 0 deletions apps/desktop/components/ui/alert-dialog/AlertDialogDescription.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import {
AlertDialogDescription,
type AlertDialogDescriptionProps,
} from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<AlertDialogDescriptionProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>

<template>
<AlertDialogDescription
v-bind="delegatedProps"
:class="cn('text-sm text-muted-foreground', props.class)"
>
<slot />
</AlertDialogDescription>
</template>
21 changes: 21 additions & 0 deletions apps/desktop/components/ui/alert-dialog/AlertDialogFooter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>

<template>
<div
:class="
cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
props.class,
)
"
>
<slot />
</div>
</template>
16 changes: 16 additions & 0 deletions apps/desktop/components/ui/alert-dialog/AlertDialogHeader.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
import type { HTMLAttributes } from 'vue'
import { cn } from '@/lib/utils'
const props = defineProps<{
class?: HTMLAttributes['class']
}>()
</script>

<template>
<div
:class="cn('flex flex-col gap-y-2 text-center sm:text-left', props.class)"
>
<slot />
</div>
</template>
22 changes: 22 additions & 0 deletions apps/desktop/components/ui/alert-dialog/AlertDialogTitle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup lang="ts">
import { type HTMLAttributes, computed } from 'vue'
import { AlertDialogTitle, type AlertDialogTitleProps } from 'radix-vue'
import { cn } from '@/lib/utils'
const props = defineProps<AlertDialogTitleProps & { class?: HTMLAttributes['class'] }>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>

<template>
<AlertDialogTitle
v-bind="delegatedProps"
:class="cn('text-lg font-semibold', props.class)"
>
<slot />
</AlertDialogTitle>
</template>
Loading

0 comments on commit 3f10d67

Please sign in to comment.