1- import { AlertCircle , Pencil , Plus , Trash2 , Wand2 } from 'lucide-react'
1+ import { AlertCircle , Eye , Pencil , Plus , Trash2 , Wand2 } from 'lucide-react'
22import { type FC , useEffect , useState } from 'react'
33import { toast } from 'sonner'
44import {
@@ -26,6 +26,7 @@ import { Label } from '@/components/ui/label'
2626import { MarkdownEditor } from '@/components/ui/MarkdownEditor'
2727import { Switch } from '@/components/ui/switch'
2828import { Textarea } from '@/components/ui/textarea'
29+ import Markdown from 'react-markdown'
2930import { type SkillDetail , type SkillMeta , useSkills } from './useSkills'
3031
3132const loadingSkillCards = [
@@ -120,6 +121,7 @@ export const SkillsPage: FC = () => {
120121 open = { isDialogOpen }
121122 onOpenChange = { setIsDialogOpen }
122123 editingSkill = { editingSkill }
124+ readOnly = { editingSkill ?. builtIn }
123125 onSave = { async ( data ) => {
124126 try {
125127 if ( editingSkill ) {
@@ -327,8 +329,11 @@ const SkillCard: FC<{
327329 onClick = { onEdit }
328330 className = "-ml-2 h-7 px-2 text-muted-foreground hover:bg-transparent hover:text-foreground"
329331 >
330- < Pencil className = "size-3.5" />
331- Edit
332+ { skill . builtIn ? (
333+ < > < Eye className = "size-3.5" /> View</ >
334+ ) : (
335+ < > < Pencil className = "size-3.5" /> Edit</ >
336+ ) }
332337 </ Button >
333338 { ! skill . builtIn ? (
334339 < Button
@@ -350,12 +355,13 @@ const SkillDialog: FC<{
350355 open : boolean
351356 onOpenChange : ( open : boolean ) => void
352357 editingSkill : SkillDetail | null
358+ readOnly ?: boolean
353359 onSave : ( data : {
354360 name : string
355361 description : string
356362 content : string
357363 } ) => Promise < void >
358- } > = ( { open, onOpenChange, editingSkill, onSave } ) => {
364+ } > = ( { open, onOpenChange, editingSkill, readOnly , onSave } ) => {
359365 const [ name , setName ] = useState ( '' )
360366 const [ description , setDescription ] = useState ( '' )
361367 const [ content , setContent ] = useState ( '' )
@@ -402,12 +408,14 @@ const SkillDialog: FC<{
402408 < DialogContent className = "flex max-h-[90vh] flex-col gap-0 overflow-hidden p-0 sm:max-w-5xl" >
403409 < DialogHeader className = "border-b px-6 py-5" >
404410 < DialogTitle >
405- { editingSkill ? 'Edit Skill' : 'Create Skill' }
411+ { readOnly ? 'View Skill' : editingSkill ? 'Edit Skill' : 'Create Skill' }
406412 </ DialogTitle >
407413 < DialogDescription >
408- { editingSkill
409- ? 'Refine when the agent should use this skill and how it should execute it.'
410- : 'Define a reusable instruction set your agent can apply when a request matches.' }
414+ { readOnly
415+ ? 'This skill is managed by BrowserOS and updated automatically.'
416+ : editingSkill
417+ ? 'Refine when the agent should use this skill and how it should execute it.'
418+ : 'Define a reusable instruction set your agent can apply when a request matches.' }
411419 </ DialogDescription >
412420 </ DialogHeader >
413421
@@ -421,6 +429,7 @@ const SkillDialog: FC<{
421429 value = { name }
422430 onChange = { ( event ) => setName ( event . target . value ) }
423431 maxLength = { 100 }
432+ readOnly = { readOnly }
424433 />
425434 < p className = "text-muted-foreground text-xs leading-5" >
426435 Keep it short and recognizable in the skills list.
@@ -436,19 +445,22 @@ const SkillDialog: FC<{
436445 onChange = { ( event ) => setDescription ( event . target . value ) }
437446 maxLength = { 500 }
438447 className = "min-h-28 resize-none bg-background"
448+ readOnly = { readOnly }
439449 />
440450 < p className = "text-muted-foreground text-xs leading-5" >
441451 This is the trigger summary the agent uses to pick the skill.
442452 </ p >
443453 </ div >
444454
445- < div className = "mt-auto rounded-lg border border-border/60 border-dashed bg-muted/30 px-3 py-2.5" >
446- < p className = "font-medium text-muted-foreground text-xs" > Tip</ p >
447- < ul className = "mt-1.5 list-disc space-y-1 pl-4 text-muted-foreground text-xs leading-5" >
448- < li > List the ordered steps the agent should follow.</ li >
449- < li > Close with the output or formatting you expect back.</ li >
450- </ ul >
451- </ div >
455+ { ! readOnly ? (
456+ < div className = "mt-auto rounded-lg border border-border/60 border-dashed bg-muted/30 px-3 py-2.5" >
457+ < p className = "font-medium text-muted-foreground text-xs" > Tip</ p >
458+ < ul className = "mt-1.5 list-disc space-y-1 pl-4 text-muted-foreground text-xs leading-5" >
459+ < li > List the ordered steps the agent should follow.</ li >
460+ < li > Close with the output or formatting you expect back.</ li >
461+ </ ul >
462+ </ div >
463+ ) : null }
452464 </ div >
453465
454466 < div className = "flex min-h-0 flex-col px-6 py-5" >
@@ -459,36 +471,52 @@ const SkillDialog: FC<{
459471 </ Badge >
460472 </ div >
461473
462- < MarkdownEditor
463- id = "skill-content"
464- value = { content }
465- onChange = { setContent }
466- onKeyDown = { handleContentKeyDown }
467- placeholder = "Write instructions for the agent. Use markdown for structure."
468- className = "mt-4 min-h-[320px] flex-1 overflow-y-auto text-sm"
469- />
474+ { readOnly ? (
475+ < div className = "prose prose-sm mt-4 min-h-[320px] max-w-none flex-1 overflow-y-auto rounded-md border p-4 text-sm dark:prose-invert" >
476+ < Markdown > { content } </ Markdown >
477+ </ div >
478+ ) : (
479+ < MarkdownEditor
480+ id = "skill-content"
481+ value = { content }
482+ onChange = { setContent }
483+ onKeyDown = { handleContentKeyDown }
484+ placeholder = "Write instructions for the agent. Use markdown for structure."
485+ className = "mt-4 min-h-[320px] flex-1 overflow-y-auto text-sm"
486+ />
487+ ) }
470488 </ div >
471489 </ div >
472490
473491 < div className = "flex flex-col gap-3 border-t px-6 py-4 sm:flex-row sm:items-center sm:justify-between" >
474492 < p className = "text-muted-foreground text-xs" >
475- Saved locally and available to your agent immediately.
493+ { readOnly
494+ ? 'This skill is managed by BrowserOS and updated automatically.'
495+ : 'Saved locally and available to your agent immediately.' }
476496 </ p >
477497 < div className = "flex flex-col-reverse gap-2 sm:flex-row" >
478- < Button
479- variant = "outline"
480- onClick = { ( ) => onOpenChange ( false ) }
481- disabled = { saving }
482- >
483- Cancel
484- </ Button >
485- < Button onClick = { handleSubmit } disabled = { ! isValid || saving } >
486- { saving
487- ? 'Saving...'
488- : editingSkill
489- ? 'Update Skill'
490- : 'Create Skill' }
491- </ Button >
498+ { readOnly ? (
499+ < Button variant = "outline" onClick = { ( ) => onOpenChange ( false ) } >
500+ Close
501+ </ Button >
502+ ) : (
503+ < >
504+ < Button
505+ variant = "outline"
506+ onClick = { ( ) => onOpenChange ( false ) }
507+ disabled = { saving }
508+ >
509+ Cancel
510+ </ Button >
511+ < Button onClick = { handleSubmit } disabled = { ! isValid || saving } >
512+ { saving
513+ ? 'Saving...'
514+ : editingSkill
515+ ? 'Update Skill'
516+ : 'Create Skill' }
517+ </ Button >
518+ </ >
519+ ) }
492520 </ div >
493521 </ div >
494522 </ DialogContent >
0 commit comments