love-ui 1.2.8 → 1.2.10
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/mcp-server.js +1 -1
- package/package.json +1 -1
- package/packages/code-block/README.md +62 -0
- package/packages/code-block/components.json +21 -0
- package/packages/code-block/package.json +46 -22
- package/packages/code-block/src/components/code-block/blocks/copy-with-select-package-manager.tsx +146 -0
- package/packages/code-block/src/components/code-block/blocks/copy-with-tabs-package-manager.tsx +110 -0
- package/packages/code-block/src/components/code-block/blocks/inline-code.tsx +28 -0
- package/packages/code-block/src/components/code-block/blocks/multi-tabs.tsx +118 -0
- package/packages/code-block/src/components/code-block/client/shiki.tsx +139 -0
- package/packages/code-block/src/components/code-block/client/sugar-high.tsx +38 -0
- package/packages/code-block/src/components/code-block/code-block.tsx +113 -0
- package/packages/code-block/src/components/code-block/copy-button.tsx +59 -0
- package/packages/code-block/src/components/code-block/mdx/pre-shiki.tsx +42 -0
- package/packages/code-block/src/components/code-block/mdx/pre-sugar-high.tsx +37 -0
- package/packages/code-block/src/components/ui/dropdown-menu.tsx +103 -0
- package/packages/code-block/src/components/ui/tabs.tsx +84 -0
- package/packages/code-block/src/index.ts +35 -0
- package/packages/code-block/src/stores/packageManager.ts +24 -0
- package/packages/code-block/src/styles/globals.css +79 -0
- package/packages/code-block/src/styles/shiki.css +95 -0
- package/packages/code-block/src/styles/sugar-high.css +48 -0
- package/packages/code-block/src/utils/cn.ts +6 -0
- package/packages/code-block/src/utils/copy.ts +9 -0
- package/packages/code-block/src/utils/react-to-text.ts +34 -0
- package/packages/code-block/src/utils/shiki/highlight.ts +47 -0
- package/packages/code-block/src/utils/sugar-high/highlight.ts +12 -0
- package/packages/code-block/tsconfig.json +13 -9
- package/packages/gooey-toast/package.json +28 -0
- package/packages/gooey-toast/src/gooey.tsx +614 -0
- package/packages/gooey-toast/src/icons.tsx +68 -0
- package/packages/gooey-toast/src/index.ts +10 -0
- package/packages/gooey-toast/src/styles.css +511 -0
- package/packages/gooey-toast/src/toast.tsx +444 -0
- package/packages/gooey-toast/src/types.ts +45 -0
- package/packages/gooey-toast/tsconfig.json +13 -0
- package/packages/gradient-mesh/index.tsx +236 -0
- package/packages/gradient-mesh/package.json +62 -0
- package/packages/gradient-mesh/tsconfig.json +10 -0
- package/packages/love-ui/src/components/site-header.tsx +3 -1
- package/packages/love-ui/src/hooks/use-media.tsx +22 -0
- package/packages/love-ui/src/styles/globals.css +3 -1
- package/packages/love-ui/src/ui/alert-dialog.tsx +3 -2
- package/packages/love-ui/src/ui/alert.tsx +5 -4
- package/packages/love-ui/src/ui/breadcrumb.tsx +7 -6
- package/packages/love-ui/src/ui/button.tsx +22 -4
- package/packages/love-ui/src/ui/card.tsx +32 -11
- package/packages/love-ui/src/ui/chart.tsx +367 -0
- package/packages/love-ui/src/ui/checkbox.tsx +42 -35
- package/packages/love-ui/src/ui/command.tsx +2 -1
- package/packages/love-ui/src/ui/dialog.tsx +5 -4
- package/packages/love-ui/src/ui/empty.tsx +7 -6
- package/packages/love-ui/src/ui/frame.tsx +7 -6
- package/packages/love-ui/src/ui/kbd.tsx +27 -0
- package/packages/love-ui/src/ui/label.tsx +2 -1
- package/packages/love-ui/src/ui/menu.tsx +2 -1
- package/packages/love-ui/src/ui/number-field.tsx +2 -1
- package/packages/love-ui/src/ui/pagination.tsx +5 -4
- package/packages/love-ui/src/ui/popover.tsx +4 -1
- package/packages/love-ui/src/ui/select.tsx +11 -2
- package/packages/love-ui/src/ui/sheet.tsx +5 -4
- package/packages/love-ui/src/ui/skeleton.tsx +3 -1
- package/packages/love-ui/src/ui/table.tsx +9 -8
- package/packages/love-ui/src/ui/textarea.tsx +2 -1
- package/packages/patterns/combobox/rich-content/combobox-rich-content-4.tsx +1 -1
- package/packages/patterns/item/interactive/item-interactive-2.tsx +1 -1
- package/packages/patterns/scroll-area/layout/scroll-area-layout-3.tsx +1 -1
- package/packages/patterns/table/standard/table-standard-1.tsx +1 -1
- package/packages/patterns/table/standard/table-standard-2.tsx +1 -1
- package/packages/shadcn-ui/components/ui/card.tsx +25 -5
- package/packages/shader-ripple/index.tsx +303 -0
- package/packages/shader-ripple/package.json +60 -0
- package/packages/shader-ripple/tsconfig.json +18 -0
- package/packages/silk/types.d.ts +16 -6
- package/packages/code-block/index.tsx +0 -638
- package/packages/code-block/server.tsx +0 -63
package/dist/mcp-server.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Server as
|
|
2
|
+
import{constants as ke}from"fs";import{access as Se,readFile as V}from"fs/promises";import c from"path";import{fileURLToPath as Pe}from"url";import{Server as be}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as je}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as Ee,ErrorCode as g,ListResourceTemplatesRequestSchema as Te,ListResourcesRequestSchema as xe,ListToolsRequestSchema as Ie,McpError as d,ReadResourceRequestSchema as _e}from"@modelcontextprotocol/sdk/types.js";var N={name:"love-ui",version:"1.2.10",private:!1,license:"AGPL-3.0-or-later",type:"module",bin:{loveui:"dist/index.js","love-ui":"dist/index.js","loveui-mcp":"dist/mcp-server.js"},files:["dist","packages","*.ts","*.tsx","*.css","*.mdx"],main:"packages/love-ui/index.ts",types:"packages/love-ui/index.ts",exports:{".":{import:"./packages/love-ui/index.ts",require:"./packages/love-ui/index.ts"},"./lib/*":"./packages/love-ui/*","./components/*":"./packages/love-ui/components/*","./shadcn-ui/*":"./packages/shadcn-ui/*","./package.json":"./package.json"},dependencies:{"@modelcontextprotocol/sdk":"^1.18.1",postcss:"^8.5.6","postcss-nested":"^7.0.2"},scripts:{build:"tsup",postbuild:"node ./scripts/copy-packages.mjs",clean:"rimraf dist packages"},devDependencies:{"@types/node":"^20.14.10",rimraf:"^6.0.1",tsup:"^8.5.0"}};import{constants as re}from"fs";import{access as ie,readFile as U,readdir as W}from"fs/promises";import I,{extname as G,join as x,relative as oe}from"path";import{fileURLToPath as ae}from"url";import q from"postcss";import ce from"postcss-nested";var le=I.dirname(ae(import.meta.url)),pe=I.resolve(le,".."),J=I.join(pe,"packages"),me=new Set([".turbo",".next",".git","dist","build","storybook-static","node_modules","__tests__","__mocks__","coverage"]),ue=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".json",".css",".scss",".sass",".mdx"]),ge=new Set(["package.json","tsconfig.json","tsconfig.build.json","tsconfig.test.json","README.md",".DS_Store"]),de=new Set(["shadcn-ui","typescript-config","patterns","loveui","love-ui"]),ye=e=>e.replace(/\\/g,"/"),H=async(e,t,s)=>{let n=await W(e,{withFileTypes:!0});for(let r of n){let o=x(e,r.name);if(r.isDirectory()){if(me.has(r.name))continue;await H(o,t,s);continue}let y=G(r.name);!ue.has(y)||ge.has(r.name)||r.name.endsWith(".d.ts")||r.name.endsWith(".test.ts")||r.name.endsWith(".test.tsx")||r.name.endsWith(".stories.tsx")||s.push({absolute:o,relative:ye(oe(t,o))})}},fe=(e,t)=>{let s=e.loveui??{},n=typeof s.target=="string"?s.target.trim():"",r=s.category;if(n.length>0){let o=typeof s.includePackageName=="boolean"?s.includePackageName:!1;return{base:n.replace(/\/+$/,""),includePackageName:o}}return r==="feature"?{base:"components",includePackageName:!0}:r==="block"?{base:"components/blocks",includePackageName:!0}:{base:"components/ui",includePackageName:!0}},he=e=>{let s=(e.loveui??{}).type;return typeof s=="string"&&s.trim().length>0?s:"registry:ui"},ve=async e=>{try{return await ie(e,re.F_OK),!0}catch{return!1}},Re=e=>e.startsWith("@loveui/")||e.startsWith("@repo/"),we=e=>e.replace(/^@repo\//,"").replace(/^@loveui\//,""),K=async()=>(await W(J,{withFileTypes:!0})).filter(t=>t.isDirectory()).map(t=>t.name).filter(t=>!de.has(t)).sort((t,s)=>t.localeCompare(s)),M=async e=>{let t=x(J,e),s=x(t,"package.json");if(!await ve(s))throw new Error(`Missing package.json for ${e}`);let n=JSON.parse(await U(s,"utf8")),r=he(n),o=Object.keys(n.dependencies??{}),y=Object.keys(n.peerDependencies??{}),D=Object.keys(n.devDependencies??{}),S=new Set([...o,...y,...D].filter(Re).filter(i=>i!=="@loveui/shadcn-ui")),A=[...new Set([...o,...y].filter(i=>!S.has(i)))],C=[...new Set(D.filter(i=>!S.has(i)&&!["@loveui/typescript-config","@types/react","@types/react-dom","typescript"].includes(i)))],P=[];for(let i of S){let h=we(i);P.push(`https://www.loveui.dev/r/${h}.json`)}let $=[];await H(t,t,$);let f=[],l={},m=fe(n,e);for(let i of $){let h=await U(i.absolute,"utf8"),b=G(i.absolute);if(b===".css"||b===".scss"||b===".sass"){let v=await q([ce]).process(h,{from:void 0});q.parse(v.css).walkAtRules("layer",j=>{let E=`@layer ${j.params}`;l[E]??={},j.walkRules(a=>{if(a.parent&&a.parent.type==="atrule"&&a.parent.name==="media")return;let w=a.selector,p={};a.walkDecls(k=>{p[k.prop]=k.value}),Object.keys(p).length>0&&(l[E][w]=p)}),j.walkAtRules("media",a=>{let w=`@media ${a.params}`,p=l[E];p[w]??={};let k=p[w];a.walkRules(L=>{let se=L.selector,T={};L.walkDecls(F=>{T[F.prop]=F.value}),Object.keys(T).length>0&&(k[se]=T)})})});continue}let u=i.relative.startsWith("src/")?i.relative.slice(4):i.relative;if(!m.includePackageName){let v=m.base.split("/").filter(Boolean),R=v[v.length-1];R&&u.startsWith(`${R}/`)&&(u=u.slice(R.length+1))}let ee=e.includes("/")?e.split("/").pop()??e:e,te=(m.includePackageName?`${m.base}/${ee}`:m.base).replace(/\/+$/,"");f.push({type:r,path:u,target:`${te}/${u}`.replace(/\/+/g,"/"),content:h})}let Z=!f.length&&Object.keys(l).length>0?"registry:style":r;return{$schema:"https://ui.shadcn.com/schema/registry-item.json",name:e,type:Z,title:n.title??e,description:n.description,author:n.author??"Connor Love <hello@loveconnor.com>",dependencies:A.length?A:void 0,devDependencies:C.length?C:void 0,registryDependencies:P.length?Array.from(new Set(P)):void 0,files:f.length?f:void 0,css:Object.keys(l).length?l:void 0}};var O="loveui://registry/",Oe="loveui://registry/{package}",B="get-loveui-package",De=e=>`${O}${e}`,Ae=c.dirname(Pe(import.meta.url)),z=c.resolve(Ae,".."),Ce=[c.join(z,"public","r"),c.resolve(z,"..","..","apps","ui","public","r"),c.resolve(process.cwd(),"apps","ui","public","r")],Y=async e=>{try{return await Se(e,ke.R_OK),!0}catch{return!1}},_=null,Q=async()=>(_||(_=(async()=>{for(let e of Ce){let t=c.join(e,"registry.json");if(await Y(t))try{let s=JSON.parse(await V(t,"utf8")),n=Array.from(new Set((s.items??[]).map(r=>r.name?.trim()).filter(r=>!!r))).sort((r,o)=>r.localeCompare(o));if(n.length===0)continue;return{dir:e,names:n,nameSet:new Set(n)}}catch{continue}}return null})()),_),$e=async()=>{let[e,t]=await Promise.all([Q(),K()]),s=new Set(t);for(let n of e?.names??[])s.add(n);return Array.from(s).sort((n,r)=>n.localeCompare(r))},Le=async e=>{let t=await Q();if(!t||!t.nameSet.has(e))return null;let s=c.join(t.dir,`${e}.json`);return await Y(s)?JSON.parse(await V(s,"utf8")):null},Fe=e=>{if(!e.startsWith(O))throw new d(g.InvalidParams,`Unsupported resource URI: ${e}`);let t=decodeURIComponent(e.slice(O.length)).trim();if(!t)throw new d(g.InvalidParams,"Package name is required.");return t},X=async e=>{try{let t=await Le(e);return t||await M(e)}catch(t){throw new d(g.InvalidParams,t instanceof Error?t.message:String(t))}};async function Ne(){let e=new be({name:"loveui-mcp",version:N.version??"0.0.0"},{capabilities:{resources:{listChanged:!0},tools:{listChanged:!0}}});e.setRequestHandler(xe,async()=>({resources:(await $e()).map(n=>({uri:De(n),name:n,description:`loveui registry definition for ${n}`,mimeType:"application/json"}))})),e.setRequestHandler(Te,async()=>({resourceTemplates:[{name:"loveui-registry",uriTemplate:Oe,description:"loveui registry definitions by package name",mimeType:"application/json"}]})),e.setRequestHandler(_e,async s=>{let n=Fe(s.params.uri),r=await X(n);return{contents:[{uri:s.params.uri,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}}),e.setRequestHandler(Ie,async()=>({tools:[{name:B,description:"Fetch a loveui registry definition by package name.",inputSchema:{type:"object",additionalProperties:!1,properties:{name:{type:"string",description:"Package name, e.g. badge"}},required:["name"]}}]})),e.setRequestHandler(Ee,async s=>{if(s.params.name!==B)throw new d(g.InvalidParams,`Tool ${s.params.name} not found`);let n=s.params.arguments?.name;if(typeof n!="string"||n.trim()==="")throw new d(g.InvalidParams,"Package name is required.");let r=await X(n.trim());return{content:[{type:"text",text:JSON.stringify(r,null,2)}]}});let t=new je;await e.connect(t)}Ne().catch(e=>{console.error(e),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# @loveui/code-blocks
|
|
2
|
+
|
|
3
|
+
React code-block components for LoveUI.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @loveui/code-blocks
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Required Styles
|
|
12
|
+
|
|
13
|
+
Import your project styles and at least one highlighter stylesheet.
|
|
14
|
+
|
|
15
|
+
```css
|
|
16
|
+
@import "@loveui/code-blocks/styles/globals.css";
|
|
17
|
+
@import "@loveui/code-blocks/styles/shiki.css";
|
|
18
|
+
/* or */
|
|
19
|
+
@import "@loveui/code-blocks/styles/sugar-high.css";
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import {
|
|
26
|
+
CodeBlock,
|
|
27
|
+
CodeBlockHeader,
|
|
28
|
+
CodeBlockContent,
|
|
29
|
+
CodeBlockGroup,
|
|
30
|
+
CodeBlockIcon,
|
|
31
|
+
CopyButton,
|
|
32
|
+
CodeblockShiki,
|
|
33
|
+
} from "@loveui/code-blocks";
|
|
34
|
+
|
|
35
|
+
const code = "console.log('LoveUI');";
|
|
36
|
+
|
|
37
|
+
export function Example() {
|
|
38
|
+
return (
|
|
39
|
+
<CodeBlock>
|
|
40
|
+
<CodeBlockHeader>
|
|
41
|
+
<CodeBlockGroup>
|
|
42
|
+
<CodeBlockIcon language="ts" />
|
|
43
|
+
<span>example.ts</span>
|
|
44
|
+
</CodeBlockGroup>
|
|
45
|
+
<CopyButton content={code} />
|
|
46
|
+
</CodeBlockHeader>
|
|
47
|
+
<CodeBlockContent>
|
|
48
|
+
<CodeblockShiki code={code} language="ts" />
|
|
49
|
+
</CodeBlockContent>
|
|
50
|
+
</CodeBlock>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Exports
|
|
56
|
+
|
|
57
|
+
- Code block primitives: `CodeBlock`, `CodeBlockHeader`, `CodeBlockContent`, `CodeBlockGroup`, `CodeBlockIcon`
|
|
58
|
+
- Highlighters: `CodeblockShiki`, `CodeBlockSugarHigh`
|
|
59
|
+
- MDX helpers: `PreShikiComponent`, `PreSugarHighComponent`
|
|
60
|
+
- Blocks: `InlineCode`, `MultiTabs`, `CodeBlockSelectPkg`, `CodeBlockTabsPkg`
|
|
61
|
+
- Utilities: `cn`, `copyToClipboard`, `reactToText`, `highlight`, `sugarHighHighlight`
|
|
62
|
+
- Store: `usePackageManager`
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "new-york",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "src/styles/globals.css",
|
|
9
|
+
"baseColor": "neutral",
|
|
10
|
+
"cssVariables": false,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"iconLibrary": "lucide",
|
|
14
|
+
"aliases": {
|
|
15
|
+
"components": "@/components",
|
|
16
|
+
"utils": "@/utils",
|
|
17
|
+
"ui": "@/components/ui",
|
|
18
|
+
"lib": "@/lib",
|
|
19
|
+
"hooks": "@/hooks"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -1,28 +1,52 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "@loveui/code-
|
|
3
|
-
"description": "Provides syntax highlighting, line numbers, and copy to clipboard functionality for code blocks.",
|
|
2
|
+
"name": "@loveui/code-blocks",
|
|
4
3
|
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"src",
|
|
8
|
+
"components.json",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"sideEffects": [
|
|
12
|
+
"**/*.css"
|
|
13
|
+
],
|
|
14
|
+
"exports": {
|
|
15
|
+
".": "./src/index.ts",
|
|
16
|
+
"./components/*": "./src/components/*",
|
|
17
|
+
"./utils/*": "./src/utils/*",
|
|
18
|
+
"./stores/*": "./src/stores/*",
|
|
19
|
+
"./styles/globals.css": "./src/styles/globals.css",
|
|
20
|
+
"./styles/shiki.css": "./src/styles/shiki.css",
|
|
21
|
+
"./styles/sugar-high.css": "./src/styles/sugar-high.css",
|
|
22
|
+
"./components.json": "./components.json"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"check-types": "tsc --noEmit"
|
|
26
|
+
},
|
|
5
27
|
"dependencies": {
|
|
6
|
-
"@
|
|
7
|
-
"@
|
|
8
|
-
"@shikijs/
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"react
|
|
13
|
-
"shiki": "3.
|
|
28
|
+
"@base-ui/react": "1.1.0",
|
|
29
|
+
"@react-symbols/icons": "1.3.0",
|
|
30
|
+
"@shikijs/langs": "3.22.0",
|
|
31
|
+
"@shikijs/themes": "3.22.0",
|
|
32
|
+
"class-variance-authority": "0.7.1",
|
|
33
|
+
"clsx": "2.1.1",
|
|
34
|
+
"lucide-react": "0.563.0",
|
|
35
|
+
"shiki": "3.22.0",
|
|
36
|
+
"sugar-high": "0.9.5",
|
|
37
|
+
"tailwind-merge": "3.4.0",
|
|
38
|
+
"tw-animate-css": "1.4.0",
|
|
39
|
+
"zustand": "5.0.11"
|
|
14
40
|
},
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"typescript": "^5.9.3"
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"react": ">=18",
|
|
43
|
+
"react-dom": ">=18",
|
|
44
|
+
"tailwindcss": ">=4"
|
|
20
45
|
},
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
]
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/mdx": "2.0.13",
|
|
48
|
+
"@types/react": "19.2.10",
|
|
49
|
+
"@types/react-dom": "19.2.3",
|
|
50
|
+
"typescript": "5.9.3"
|
|
51
|
+
}
|
|
28
52
|
}
|
package/packages/code-block/src/components/code-block/blocks/copy-with-select-package-manager.tsx
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, type FC, type SVGProps } from "react";
|
|
4
|
+
import {
|
|
5
|
+
usePackageManager,
|
|
6
|
+
type PackageManager,
|
|
7
|
+
} from "../../../stores/packageManager";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
CodeBlock,
|
|
11
|
+
CodeBlockContent,
|
|
12
|
+
CodeBlockHeader,
|
|
13
|
+
CodeBlockIcon,
|
|
14
|
+
} from "../code-block";
|
|
15
|
+
import { CopyButton } from "../copy-button";
|
|
16
|
+
import { CodeblockShiki } from "../client/shiki";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
DropdownMenu,
|
|
20
|
+
DropdownMenuContent,
|
|
21
|
+
DropdownMenuItem,
|
|
22
|
+
DropdownMenuTrigger,
|
|
23
|
+
} from "../../ui/dropdown-menu";
|
|
24
|
+
|
|
25
|
+
import { cn } from "../../../utils/cn";
|
|
26
|
+
import { Bun, NPM, PNPM, Yarn } from "@react-symbols/icons";
|
|
27
|
+
import { CheckIcon, ChevronDownIcon } from "lucide-react";
|
|
28
|
+
|
|
29
|
+
interface CodeBlockSelectPkgProps {
|
|
30
|
+
command: string;
|
|
31
|
+
title: string;
|
|
32
|
+
type: "install" | "dlx";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface Command {
|
|
36
|
+
name: PackageManager;
|
|
37
|
+
install: string;
|
|
38
|
+
icon: FC<SVGProps<SVGSVGElement>>;
|
|
39
|
+
dlx: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const Commands: Command[] = [
|
|
43
|
+
{
|
|
44
|
+
name: "npm",
|
|
45
|
+
install: "npm i",
|
|
46
|
+
icon: NPM,
|
|
47
|
+
dlx: "npx",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "pnpm",
|
|
51
|
+
install: "pnpm i",
|
|
52
|
+
icon: PNPM,
|
|
53
|
+
dlx: "pnpm dlx",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: "yarn",
|
|
57
|
+
install: "yarn add",
|
|
58
|
+
icon: Yarn,
|
|
59
|
+
dlx: "yarn dlx",
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "bun",
|
|
63
|
+
install: "bun add",
|
|
64
|
+
icon: Bun,
|
|
65
|
+
dlx: "bunx --bun",
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
const SelectPackageManager = () => {
|
|
70
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
71
|
+
const { packageManager, setPackageManager } = usePackageManager();
|
|
72
|
+
|
|
73
|
+
const selectedPkg =
|
|
74
|
+
Commands.find((pkg) => pkg.name === packageManager) ?? Commands[0];
|
|
75
|
+
const Icon = selectedPkg.icon;
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
|
|
79
|
+
<DropdownMenuTrigger
|
|
80
|
+
title="Select Package Manager"
|
|
81
|
+
className="group flex cursor-pointer items-center space-x-1 px-2"
|
|
82
|
+
>
|
|
83
|
+
<Icon className="size-4" />
|
|
84
|
+
<ChevronDownIcon
|
|
85
|
+
size={13}
|
|
86
|
+
className={cn(
|
|
87
|
+
"transform transition-transform duration-200 ease-in-out",
|
|
88
|
+
"group-hover:text-black dark:group-hover:text-white",
|
|
89
|
+
isOpen && "rotate-180 text-black dark:text-white",
|
|
90
|
+
)}
|
|
91
|
+
/>
|
|
92
|
+
</DropdownMenuTrigger>
|
|
93
|
+
<DropdownMenuContent align="end" alignOffset={2}>
|
|
94
|
+
{Commands.map((pkg) => {
|
|
95
|
+
const PkgIcon = pkg.icon;
|
|
96
|
+
return (
|
|
97
|
+
<DropdownMenuItem
|
|
98
|
+
key={pkg.name}
|
|
99
|
+
title={`Using ${pkg.name}`}
|
|
100
|
+
onClick={() => setPackageManager(pkg.name)}
|
|
101
|
+
className="flex w-full items-center justify-between"
|
|
102
|
+
>
|
|
103
|
+
<div className="flex items-center space-x-2">
|
|
104
|
+
<PkgIcon className="size-4" />
|
|
105
|
+
<span>{pkg.name}</span>
|
|
106
|
+
</div>
|
|
107
|
+
{selectedPkg.name === pkg.name && <CheckIcon width={14} />}
|
|
108
|
+
</DropdownMenuItem>
|
|
109
|
+
);
|
|
110
|
+
})}
|
|
111
|
+
</DropdownMenuContent>
|
|
112
|
+
</DropdownMenu>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const CodeBlockSelectPkg = ({
|
|
117
|
+
title,
|
|
118
|
+
type,
|
|
119
|
+
command,
|
|
120
|
+
}: CodeBlockSelectPkgProps) => {
|
|
121
|
+
const { packageManager } = usePackageManager();
|
|
122
|
+
|
|
123
|
+
const selectedPkg =
|
|
124
|
+
Commands.find((pkg) => pkg.name === packageManager) ?? Commands[0];
|
|
125
|
+
const fullCommand = `${selectedPkg[type]} ${command}`;
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<CodeBlock>
|
|
129
|
+
<CodeBlockHeader>
|
|
130
|
+
<div className="flex items-center space-x-2">
|
|
131
|
+
<CodeBlockIcon language="bash" />
|
|
132
|
+
<span className="font-medium">{title}</span>
|
|
133
|
+
</div>
|
|
134
|
+
<div className="flex items-center space-x-2 divide-x divide-neutral-300 dark:divide-neutral-700">
|
|
135
|
+
<SelectPackageManager />
|
|
136
|
+
<CopyButton className="pl-1" content={fullCommand} />
|
|
137
|
+
</div>
|
|
138
|
+
</CodeBlockHeader>
|
|
139
|
+
<CodeBlockContent>
|
|
140
|
+
<CodeblockShiki language="bash" code={fullCommand} />
|
|
141
|
+
</CodeBlockContent>
|
|
142
|
+
</CodeBlock>
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export { CodeBlockSelectPkg, SelectPackageManager };
|
package/packages/code-block/src/components/code-block/blocks/copy-with-tabs-package-manager.tsx
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { FC, SVGProps } from "react";
|
|
4
|
+
import {
|
|
5
|
+
usePackageManager,
|
|
6
|
+
type PackageManager,
|
|
7
|
+
} from "../../../stores/packageManager";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
CodeBlock,
|
|
11
|
+
CodeBlockContent,
|
|
12
|
+
CodeBlockHeader,
|
|
13
|
+
CodeBlockIcon,
|
|
14
|
+
} from "../code-block";
|
|
15
|
+
import { CopyButton } from "../copy-button";
|
|
16
|
+
import { CodeblockShiki } from "../client/shiki";
|
|
17
|
+
|
|
18
|
+
import { Bun, NPM, PNPM, Yarn } from "@react-symbols/icons";
|
|
19
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../ui/tabs";
|
|
20
|
+
|
|
21
|
+
interface Command {
|
|
22
|
+
name: PackageManager;
|
|
23
|
+
install: string;
|
|
24
|
+
icon: FC<SVGProps<SVGSVGElement>>;
|
|
25
|
+
dlx: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface CodeBlockTabsPkgProps {
|
|
29
|
+
command: string;
|
|
30
|
+
type: "install" | "dlx";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const Commands: Command[] = [
|
|
34
|
+
{
|
|
35
|
+
name: "npm",
|
|
36
|
+
install: "npm i",
|
|
37
|
+
icon: NPM,
|
|
38
|
+
dlx: "npx",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "pnpm",
|
|
42
|
+
install: "pnpm i",
|
|
43
|
+
icon: PNPM,
|
|
44
|
+
dlx: "pnpm dlx",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "yarn",
|
|
48
|
+
install: "yarn add",
|
|
49
|
+
icon: Yarn,
|
|
50
|
+
dlx: "yarn dlx",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "bun",
|
|
54
|
+
install: "bun add",
|
|
55
|
+
icon: Bun,
|
|
56
|
+
dlx: "bunx --bun",
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const CodeBlockTabsPkg = ({ command, type }: CodeBlockTabsPkgProps) => {
|
|
61
|
+
const { packageManager, setPackageManager } = usePackageManager();
|
|
62
|
+
|
|
63
|
+
const selectedPkg =
|
|
64
|
+
Commands.find((pkg) => pkg.name === packageManager) ?? Commands[0];
|
|
65
|
+
const fullCommand = `${selectedPkg[type]} ${command}`;
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Tabs
|
|
69
|
+
className="w-full gap-1"
|
|
70
|
+
value={packageManager}
|
|
71
|
+
onValueChange={(value) => setPackageManager(value as PackageManager)}
|
|
72
|
+
>
|
|
73
|
+
<CodeBlock>
|
|
74
|
+
<CodeBlockHeader>
|
|
75
|
+
<div className="flex items-center space-x-1">
|
|
76
|
+
<CodeBlockIcon language="bash" />
|
|
77
|
+
<TabsList className="gap-1 border-0 bg-transparent dark:bg-transparent">
|
|
78
|
+
{Commands.map((cmd) => {
|
|
79
|
+
const Icon = cmd.icon;
|
|
80
|
+
return (
|
|
81
|
+
<TabsTrigger
|
|
82
|
+
key={cmd.name}
|
|
83
|
+
value={cmd.name}
|
|
84
|
+
className="data-[state=active]:shadow-none"
|
|
85
|
+
>
|
|
86
|
+
<Icon className="size-4" />
|
|
87
|
+
<span>{cmd.name}</span>
|
|
88
|
+
</TabsTrigger>
|
|
89
|
+
);
|
|
90
|
+
})}
|
|
91
|
+
</TabsList>
|
|
92
|
+
</div>
|
|
93
|
+
<CopyButton className="pl-1" content={fullCommand} />
|
|
94
|
+
</CodeBlockHeader>
|
|
95
|
+
<CodeBlockContent>
|
|
96
|
+
{Commands.map((cmd) => (
|
|
97
|
+
<TabsContent key={cmd.name} value={cmd.name} className="mt-0">
|
|
98
|
+
<CodeblockShiki
|
|
99
|
+
language="bash"
|
|
100
|
+
code={`${cmd[type]} ${command}`}
|
|
101
|
+
/>
|
|
102
|
+
</TabsContent>
|
|
103
|
+
))}
|
|
104
|
+
</CodeBlockContent>
|
|
105
|
+
</CodeBlock>
|
|
106
|
+
</Tabs>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export { CodeBlockTabsPkg };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { Languages } from "../../../utils/shiki/highlight";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
CodeBlock,
|
|
7
|
+
CodeBlockContent,
|
|
8
|
+
} from "../code-block";
|
|
9
|
+
import { CopyButton } from "../copy-button";
|
|
10
|
+
import { CodeblockShiki } from "../client/shiki";
|
|
11
|
+
|
|
12
|
+
interface InlineCodeProps {
|
|
13
|
+
code: string;
|
|
14
|
+
language?: Languages;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const InlineCode = ({ code, language = "bash" }: InlineCodeProps) => {
|
|
18
|
+
return (
|
|
19
|
+
<CodeBlock>
|
|
20
|
+
<CodeBlockContent className="flex w-full items-center justify-between">
|
|
21
|
+
<CodeblockShiki language={language} code={code} />
|
|
22
|
+
<CopyButton className="px-3" content={code} />
|
|
23
|
+
</CodeBlockContent>
|
|
24
|
+
</CodeBlock>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default InlineCode;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type { Languages } from "../../../utils/shiki/highlight";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CodeBlock,
|
|
5
|
+
CodeBlockContent,
|
|
6
|
+
CodeBlockHeader,
|
|
7
|
+
CodeBlockIcon,
|
|
8
|
+
} from "../code-block";
|
|
9
|
+
import { CopyButton } from "../copy-button";
|
|
10
|
+
import { CodeblockShiki } from "../client/shiki";
|
|
11
|
+
|
|
12
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../../ui/tabs";
|
|
13
|
+
|
|
14
|
+
const Code = [
|
|
15
|
+
{
|
|
16
|
+
title: "layout.tsx",
|
|
17
|
+
lang: "tsx",
|
|
18
|
+
code: `import "./globals.css";
|
|
19
|
+
|
|
20
|
+
export default function RootLayout({
|
|
21
|
+
children,
|
|
22
|
+
}: Readonly<{
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}>) {
|
|
25
|
+
return (
|
|
26
|
+
<html lang="en">
|
|
27
|
+
<body>{children}</body>
|
|
28
|
+
</html>
|
|
29
|
+
);
|
|
30
|
+
}`,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
title: "page.tsx",
|
|
34
|
+
lang: "tsx",
|
|
35
|
+
code: `export default function Home() {
|
|
36
|
+
return (
|
|
37
|
+
<div>
|
|
38
|
+
<p>Home page</p>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}`,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
title: "globals.css",
|
|
45
|
+
lang: "css",
|
|
46
|
+
code: `@import "tailwindcss";
|
|
47
|
+
|
|
48
|
+
:root {
|
|
49
|
+
--background: #ffffff;
|
|
50
|
+
--foreground: #171717;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@theme inline {
|
|
54
|
+
--color-background: var(--background);
|
|
55
|
+
--color-foreground: var(--foreground);
|
|
56
|
+
--font-sans: var(--font-geist-sans);
|
|
57
|
+
--font-mono: var(--font-geist-mono);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@media (prefers-color-scheme: dark) {
|
|
61
|
+
:root {
|
|
62
|
+
--background: #0a0a0a;
|
|
63
|
+
--foreground: #ededed;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
body {
|
|
68
|
+
background: var(--background);
|
|
69
|
+
color: var(--foreground);
|
|
70
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
71
|
+
}`,
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
const CopyWithTabsCode = () => {
|
|
76
|
+
return (
|
|
77
|
+
<Tabs className="w-full gap-1">
|
|
78
|
+
<CodeBlock>
|
|
79
|
+
<CodeBlockHeader>
|
|
80
|
+
<div className="flex items-center space-x-1">
|
|
81
|
+
<TabsList className="gap-1 border-0 bg-transparent dark:bg-transparent">
|
|
82
|
+
{Code.map((c) => {
|
|
83
|
+
return (
|
|
84
|
+
<TabsTrigger
|
|
85
|
+
value={c.title}
|
|
86
|
+
key={c.title}
|
|
87
|
+
className="data-[state=active]:bg-transparent"
|
|
88
|
+
>
|
|
89
|
+
<CodeBlockIcon language={c.lang} />
|
|
90
|
+
<span>{c.title}</span>
|
|
91
|
+
</TabsTrigger>
|
|
92
|
+
);
|
|
93
|
+
})}
|
|
94
|
+
</TabsList>
|
|
95
|
+
</div>
|
|
96
|
+
</CodeBlockHeader>
|
|
97
|
+
<CodeBlockContent className="relative">
|
|
98
|
+
{Code.map((cmd) => (
|
|
99
|
+
<TabsContent key={cmd.title} value={cmd.title}>
|
|
100
|
+
<div className="relative">
|
|
101
|
+
<CopyButton
|
|
102
|
+
content={cmd.code}
|
|
103
|
+
className="sticky top-2.5 right-2.5 z-10 float-right -mb-10 p-1"
|
|
104
|
+
/>
|
|
105
|
+
<CodeblockShiki
|
|
106
|
+
code={cmd.code}
|
|
107
|
+
language={cmd.lang as Languages}
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
</TabsContent>
|
|
111
|
+
))}
|
|
112
|
+
</CodeBlockContent>
|
|
113
|
+
</CodeBlock>
|
|
114
|
+
</Tabs>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export default CopyWithTabsCode;
|