6 Commits
v1.0.0 ... main

Author SHA1 Message Date
729ca6e072 Version 1.1.2 2026-03-12 11:31:02 +01:00
ca8f863618 Correctly supply colored table headers 2026-03-12 11:30:43 +01:00
c76e9653d4 Fixed table styles and panel variants 2026-03-12 11:17:43 +01:00
bdc2a2b423 Shuffle 2026-03-10 20:15:28 +01:00
a30706b60a chore(release): v1.0.2 2026-03-10 10:42:16 +01:00
df8e69dcf9 Bundle scripts 2026-03-10 10:37:21 +01:00
14 changed files with 110 additions and 48 deletions

2
.gitignore vendored
View File

@@ -141,7 +141,7 @@ out
# Nuxt.js build / generate output # Nuxt.js build / generate output
.nuxt .nuxt
dist dist/*
# Gatsby files # Gatsby files
.cache/ .cache/

View File

@@ -6,6 +6,11 @@
"stylelint-scss", "stylelint-scss",
"stylelint-order" "stylelint-order"
], ],
"ignoreFiles": [
"**/node_modules/**",
"dist/**",
"src/vendor/**"
],
"customSyntax": "postcss-scss", "customSyntax": "postcss-scss",
"rules": { "rules": {
"at-rule-no-unknown": null, "at-rule-no-unknown": null,

View File

@@ -1,7 +1,7 @@
{ {
"name": "cyberduck", "name": "cyberduck",
"private": false, "private": true,
"version": "1.0.0", "version": "1.1.2",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://git.maze.io/maze/cyberduck.git" "url": "git+https://git.maze.io/maze/cyberduck.git"
@@ -12,7 +12,7 @@
"homepage": "https://git.maze.io/maze/cyberduck#readme", "homepage": "https://git.maze.io/maze/cyberduck#readme",
"type": "module", "type": "module",
"main": "dist/cyberduck.cjs.js", "main": "dist/cyberduck.cjs.js",
"module": "dist/cyberduck.es.js", "module": "dist/esm.js",
"types": "dist/types/index.d.ts", "types": "dist/types/index.d.ts",
"style": "dist/cyberduck.css", "style": "dist/cyberduck.css",
"sideEffects": [ "sideEffects": [
@@ -21,23 +21,24 @@
], ],
"files": [ "files": [
"dist", "dist",
"src/styles" "src/styles",
"scripts"
], ],
"exports": { "exports": {
".": { ".": {
"import": "./dist/cyberduck.es.js", "types": "./dist/types/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/cyberduck.cjs.js", "require": "./dist/cyberduck.cjs.js",
"default": "./dist/cyberduck.es.js", "default": "./dist/index.js"
"types": "./dist/types/index.d.ts"
}, },
"./package.json": "./package.json", "./package.json": "./package.json",
"./styles": "./src/styles/cyberduck.scss", "./styles": "./src/styles/cyberduck.scss",
"./styles/*": "./src/styles/*", "./styles/*": "./src/styles/*",
"./css": "./dist/cyberduck.css", "./css": "./dist/cyberduck.css",
"./*": { "./*": {
"import": "./dist/cyberduck.es.js", "types": "./dist/*.d.ts",
"require": "./dist/cyberduck.cjs.js", "import": "./dist/index.js",
"types": "./dist/*.d.ts" "require": "./dist/cyberduck.cjs.js"
} }
}, },
"scripts": { "scripts": {
@@ -48,9 +49,9 @@
"lint": "eslint .", "lint": "eslint .",
"lint:css": "stylelint \"src/**/*.{scss,css,sass}\"", "lint:css": "stylelint \"src/**/*.{scss,css,sass}\"",
"format:css": "prettier --write \"src/**/*.{scss,css,sass}\"", "format:css": "prettier --write \"src/**/*.{scss,css,sass}\"",
"styles:build": "sass --no-source-map --style=compressed --load-path=node_modules src:dist", "styles:build": "sass --no-source-map --style=compressed --load-path=node_modules --quiet-deps src:dist",
"styles:check": "sass --no-source-map --update --load-path=node_modules src:dist", "styles:check": "sass --no-source-map --update --load-path=node_modules --quiet-deps src:dist",
"styles:dev": "sass --watch --no-source-map --load-path=node_modules src:dist", "styles:dev": "sass --watch --no-source-map --load-path=node_modules --quiet-deps src:dist",
"preview": "vite preview" "preview": "vite preview"
}, },
"peerDependencies": { "peerDependencies": {
@@ -83,11 +84,6 @@
"lint-staged": "^16.3.2", "lint-staged": "^16.3.2",
"postcss-scss": "^4.0.9", "postcss-scss": "^4.0.9",
"prettier": "^3.8.1", "prettier": "^3.8.1",
"react": "^19.2.4",
"react-bootstrap": "^2.10.10",
"react-dom": "^19.2.4",
"react-router": "^7.13.1",
"react-router-dom": "^7.13.1",
"sass": "^1.97.3", "sass": "^1.97.3",
"stylelint": "^17.4.0", "stylelint": "^17.4.0",
"stylelint-config-standard-scss": "^17.0.0", "stylelint-config-standard-scss": "^17.0.0",

View File

@@ -29,7 +29,7 @@ if (fs.existsSync(tsconfig)) {
// 2) Sass build (styles) // 2) Sass build (styles)
runIfAvailable( runIfAvailable(
'npx --no-install sass --version', 'npx --no-install sass --version',
'npx --no-install sass --no-source-map --style=compressed --load-path=node_modules src:dist', 'npx --no-install sass --no-source-map --style=compressed --load-path=node_modules --quiet-deps --silence-deprecation=if-function,global-builtin,color-functions src:dist',
'Sass styles build', 'Sass styles build',
) )
@@ -68,3 +68,49 @@ try {
} catch (e) { } catch (e) {
console.warn('Failed to generate per-subpath wrappers:', e && e.message) console.warn('Failed to generate per-subpath wrappers:', e && e.message)
} }
// Ensure a minimal `dist/types/index.d.ts` exists so consumers installing
// from the packed tarball won't see a "could not find declaration file" error.
try {
const indexDts = path.join(cwd, 'dist', 'types', 'index.d.ts')
if (!fs.existsSync(indexDts)) {
const dir = path.dirname(indexDts)
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
const fallback = `/// <reference types="react" />
export const Navbar: import('react').FC<any>;
export const Footer: import('react').FC<any>;
export const Layout: import('react').FC<any>;
export const Split: import('react').FC<any> & { Panel: import('react').FC<any> };
export default { Navbar, Footer, Layout, Split };
`
fs.writeFileSync(indexDts, fallback)
console.log('Wrote fallback dist/types/index.d.ts')
}
} catch (e) {
console.warn('Failed to write fallback index.d.ts:', e && e.message)
}
// Write a small ESM wrapper that re-exports everything from the generated
// `cyberduck.es.js` bundle. Some browser toolchains import the package
// entry directly; having a tiny wrapper helps ensure named exports are
// preserved and visible to native ESM loaders.
try {
const esmWrapperPath = path.join(cwd, 'dist', 'esm.js')
// Re-export named bindings and build a local default object so we don't
// need to import the bundle's default (some tools choke on that).
const esmContent = `import { Navbar, Footer, Layout, Split } from './cyberduck.es.js';\nexport { Navbar, Footer, Layout, Split };\nconst __default = { Navbar, Footer, Layout, Split };\nexport default __default;\n`;
fs.writeFileSync(esmWrapperPath, esmContent)
console.log('Wrote dist/esm.js wrapper')
} catch (e) {
console.warn('Failed to write dist/esm.js wrapper:', e && e.message)
}
try {
const indexPath = path.join(cwd, 'dist', 'index.js')
// index.js mirrors esm.js to help environments that resolve package root
// to an index file instead of reading package.json fields.
const indexContent = `import { Navbar, Footer, Layout, Split } from './cyberduck.es.js';\nexport { Navbar, Footer, Layout, Split };\nconst __default = { Navbar, Footer, Layout, Split };\nexport default __default;\n`;
fs.writeFileSync(indexPath, indexContent)
console.log('Wrote dist/index.js wrapper')
} catch (e) {
console.warn('Failed to write dist/index.js wrapper:', e && e.message)
}

View File

@@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
const Footer: React.FC = () => { export const Footer: React.FC = () => {
return ( return (
<footer className="footer mt-auto py-3 bg-dark text-light"> <footer className="footer mt-auto py-3 bg-dark text-light">
<div className="container text-center">© CyberDuck</div> <div className="container text-center">© CyberDuck</div>

View File

@@ -2,11 +2,11 @@ import React from 'react'
import Navbar from './Navbar' import Navbar from './Navbar'
import Footer from './Footer' import Footer from './Footer'
interface LayoutProps { export interface LayoutProps {
children: React.ReactNode children: React.ReactNode
} }
const Layout: React.FC<LayoutProps> = ({ children }) => { export const Layout: React.FC<LayoutProps> = ({ children }) => {
return ( return (
<div className="app-layout d-flex flex-column"> <div className="app-layout d-flex flex-column">
<Navbar /> <Navbar />

View File

@@ -1,6 +1,6 @@
import React from 'react' import React from 'react'
const Navbar: React.FC = () => { export const Navbar: React.FC = () => {
return ( return (
<nav className="navbar navbar-expand-lg navbar-dark bg-dark"> <nav className="navbar navbar-expand-lg navbar-dark bg-dark">
<div className="container-fluid"> <div className="container-fluid">

View File

@@ -15,7 +15,7 @@ export type AccentKey =
export interface PanelProps extends React.HTMLAttributes<HTMLDivElement> { export interface PanelProps extends React.HTMLAttributes<HTMLDivElement> {
size?: number size?: number
variant?: 'border' | 'solid' variant?: '' | 'border' | 'solid'
accent?: AccentKey accent?: AccentKey
children?: React.ReactNode children?: React.ReactNode
} }
@@ -23,13 +23,13 @@ export interface PanelProps extends React.HTMLAttributes<HTMLDivElement> {
const Panel: React.FC<PanelProps> = ({ children }) => <>{children}</> const Panel: React.FC<PanelProps> = ({ children }) => <>{children}</>
Panel.displayName = 'SplitPanel' Panel.displayName = 'SplitPanel'
interface SplitProps { export interface SplitProps {
direction?: 'horizontal' | 'vertical' direction?: 'horizontal' | 'vertical'
gutter?: string | number gutter?: string | number
className?: string className?: string
// allow any children but we will look for Panel elements // allow any children but we will look for Panel elements
children?: React.ReactNode children?: React.ReactNode
variant?: 'border' | 'solid' variant?: '' | 'border' | 'solid'
accent?: AccentKey accent?: AccentKey
} }
@@ -53,7 +53,7 @@ const Split: React.FC<SplitProps> & { Panel: React.FC<PanelProps> } = ({
// detect Panel children and extract sizes and passthrough props // detect Panel children and extract sizes and passthrough props
const sizes: (number | null)[] = childrenArray.map((c) => { const sizes: (number | null)[] = childrenArray.map((c) => {
if (React.isValidElement(c) && (c.type as any).displayName === 'SplitPanel') { if (React.isValidElement(c) && (c.type as any)?.displayName === 'SplitPanel') {
const p = c as React.ReactElement<PanelProps> const p = c as React.ReactElement<PanelProps>
return typeof p.props.size === 'number' ? p.props.size : 1 return typeof p.props.size === 'number' ? p.props.size : 1
} }
@@ -71,7 +71,7 @@ const Split: React.FC<SplitProps> & { Panel: React.FC<PanelProps> } = ({
let extraClass = '' let extraClass = ''
let extraStyle: React.CSSProperties | undefined let extraStyle: React.CSSProperties | undefined
if (React.isValidElement(child) && (child.type as any).displayName === 'SplitPanel') { if (React.isValidElement(child) && (child.type as any)?.displayName === 'SplitPanel') {
const p = child as React.ReactElement<PanelProps> const p = child as React.ReactElement<PanelProps>
extraClass = p.props.className || '' extraClass = p.props.className || ''
extraStyle = p.props.style extraStyle = p.props.style
@@ -90,7 +90,9 @@ const Split: React.FC<SplitProps> & { Panel: React.FC<PanelProps> } = ({
} }
const mergedStyle = extraStyle ? { ...style, ...extraStyle } : style const mergedStyle = extraStyle ? { ...style, ...extraStyle } : style
const childElem = React.isValidElement(child) ? (child as React.ReactElement<PanelProps>) : null const childElem = React.isValidElement(child) && (child.type as any)?.displayName === 'SplitPanel'
? (child as React.ReactElement<PanelProps>)
: null
const variant = childElem ? childElem.props.variant ?? splitVariant : splitVariant const variant = childElem ? childElem.props.variant ?? splitVariant : splitVariant
// Determine accent: a panel with its own `accent` always uses it. // Determine accent: a panel with its own `accent` always uses it.
// Otherwise, if the parent `Split` has an `accent`, only the first // Otherwise, if the parent `Split` has an `accent`, only the first
@@ -121,16 +123,13 @@ const Split: React.FC<SplitProps> & { Panel: React.FC<PanelProps> } = ({
accentBR = true accentBR = true
} }
} }
let variantClass = '' let variantClass = variant === '' ? '' : `pane--${variant}`
if (variant === 'border') variantClass = 'pane--border'
else if (variant === 'solid') variantClass = 'pane--solid'
const paneClass = `pane ${variantClass} ${extraClass} ${accentTL ? 'pane--accent--tl' : ''} ${accentBR ? 'pane--accent--br' : ''}`.trim() const paneClass = `pane ${variantClass} ${extraClass} ${accentTL ? 'pane--accent--tl' : ''} ${accentBR ? 'pane--accent--br' : ''}`.trim()
// attach accent color as CSS var for the mixin to pick up // attach accent color as CSS var for the mixin to pick up
const finalStyle = { ...mergedStyle } const finalStyle: React.CSSProperties = { ...mergedStyle }
if (accent) { if (accent) {
;(finalStyle as any)['--pane-accent-color'] = `var(--color-variant-${accent})` (finalStyle as any)['--pane-accent-color'] = `var(--color-variant-${accent})`
} }
return ( return (

View File

@@ -1,8 +1,10 @@
// Library entry: re-export key components and include styles import Navbar from './components/Navbar.tsx'
export { default as Navbar } from './components/Layout/Navbar' import Footer from './components/Footer.tsx'
export { default as Footer } from './components/Layout/Footer' import Layout from './components/Layout.tsx'
export { default as Layout } from './components/Layout/Layout' import Split from './components/Split.tsx'
export { default as Split } from './components/Layout/Split'
// also export named components so consumers can import { Split } from 'cyberduck'
export { Navbar, Footer, Layout, Split }
// Styles are optional — consumers can import the CSS or SCSS themselves: // Styles are optional — consumers can import the CSS or SCSS themselves:
// import 'cyberduck/css' // import 'cyberduck/css'

View File

@@ -1,5 +1,5 @@
import Layout from '../components/Layout/Layout' import Layout from '../components/Layout'
import Split from '../components/Layout/Split' import Split from '../components/Split'
export default function LayoutHorizontal() { export default function LayoutHorizontal() {
return ( return (

View File

@@ -1,5 +1,5 @@
import Layout from '../components/Layout/Layout' import Layout from '../components/Layout'
import Split from '../components/Layout/Split' import Split from '../components/Split'
export default function LayoutShowcase() { export default function LayoutShowcase() {
return ( return (

View File

@@ -1,5 +1,5 @@
import Layout from '../components/Layout/Layout' import Layout from '../components/Layout'
import Split from '../components/Layout/Split' import Split from '../components/Split'
export default function LayoutVertical() { export default function LayoutVertical() {
return ( return (

View File

@@ -1,4 +1,5 @@
@use '../variables' as vars; @use '../variables' as vars;
@use 'sass:map' as map;
// Table overrides scaffold // Table overrides scaffold
table { table {
@@ -27,7 +28,7 @@ table {
th *, th *,
td, td,
td * { td * {
color: var(--color-variant-green) !important; color: var(--color-variant-green);
} }
} }
@@ -38,3 +39,8 @@ table {
} }
} }
} }
// Override default table text color for better contrast with CyberDuck palette (especially on striped rows)
.table > thead > tr > th {
color: map.get(vars.$cyberduck-neon, green);
}

View File

@@ -28,6 +28,7 @@ export default defineConfig(({ command }) => ({
'react-bootstrap', 'react-bootstrap',
], ],
output: { output: {
exports: 'named',
globals: { globals: {
react: 'React', react: 'React',
'react-dom': 'ReactDOM', 'react-dom': 'ReactDOM',
@@ -35,4 +36,11 @@ export default defineConfig(({ command }) => ({
}, },
}, },
}, },
css: {
preprocessorOptions: {
scss: {
quietDeps: true,
},
},
},
})) }))