eidosui 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl
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.
- eidos/__init__.py +2 -57
- eidos/components/headers.py +29 -0
- eidos/css/styles.css +337 -0
- eidos/css/themes/dark.css +119 -0
- eidos/css/themes/eidos-variables.css +190 -0
- eidos/css/themes/light.css +80 -0
- eidos/styles.py +80 -0
- eidos/tags.py +99 -0
- eidos/utils.py +63 -0
- eidosui-0.2.0.dist-info/METADATA +124 -0
- eidosui-0.2.0.dist-info/RECORD +13 -0
- eidosui-0.2.0.dist-info/licenses/LICENSE +21 -0
- eidos/components/__init__.py +0 -22
- eidos/components/forms.py +0 -57
- eidos/components/typography.py +0 -85
- eidos/core/__init__.py +0 -25
- eidos/core/helpers.py +0 -87
- eidos/core/styles.py +0 -92
- eidos/core/utils.py +0 -46
- eidos/static/eidos-ui.js +0 -74
- eidos/themes/dark.css +0 -52
- eidos/themes/light.css +0 -110
- eidosui-0.1.0.dist-info/METADATA +0 -61
- eidosui-0.1.0.dist-info/RECORD +0 -14
- {eidosui-0.1.0.dist-info → eidosui-0.2.0.dist-info}/WHEEL +0 -0
eidos/components/__init__.py
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
"""EidosUI Components - Simple MVP"""
|
2
|
-
|
3
|
-
# Semantic Typography
|
4
|
-
from .typography import (
|
5
|
-
H1, H2, H3, H4, H5, H6,
|
6
|
-
P, Text, Em, Strong, A,
|
7
|
-
Code, Pre, Mark, Small
|
8
|
-
)
|
9
|
-
|
10
|
-
# Forms
|
11
|
-
from .forms import (
|
12
|
-
Button, Input, Textarea, Select, Label, FormGroup
|
13
|
-
)
|
14
|
-
|
15
|
-
__all__ = [
|
16
|
-
# Typography
|
17
|
-
"H1", "H2", "H3", "H4", "H5", "H6",
|
18
|
-
"P", "Text", "Em", "Strong", "A",
|
19
|
-
"Code", "Pre", "Mark", "Small",
|
20
|
-
# Forms
|
21
|
-
"Button", "Input", "Textarea", "Select", "Label", "FormGroup",
|
22
|
-
]
|
eidos/components/forms.py
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
"""Form components for EidosUI"""
|
2
|
-
|
3
|
-
import fastapi_tags as ft
|
4
|
-
from ..core.styles import button_styles, form_styles
|
5
|
-
from ..core.utils import merge_classes
|
6
|
-
|
7
|
-
|
8
|
-
def Button(*content, cls: str = button_styles.primary, size_cls: str = button_styles.md, **kwargs) -> ft.Button:
|
9
|
-
"""Highly flexible button component that takes classes directly"""
|
10
|
-
return ft.Button(
|
11
|
-
*content,
|
12
|
-
cls=merge_classes(cls, size_cls),
|
13
|
-
**kwargs
|
14
|
-
)
|
15
|
-
|
16
|
-
|
17
|
-
def Input(cls: str = form_styles.input, size_cls: str = "", state_cls: str = "", **kwargs) -> ft.Input:
|
18
|
-
"""Flexible input component with class-based styling"""
|
19
|
-
return ft.Input(
|
20
|
-
cls=merge_classes(cls, size_cls, state_cls),
|
21
|
-
**kwargs
|
22
|
-
)
|
23
|
-
|
24
|
-
|
25
|
-
def Textarea(cls: str = form_styles.textarea, **kwargs) -> ft.Textarea:
|
26
|
-
"""Flexible textarea component"""
|
27
|
-
return ft.Textarea(
|
28
|
-
cls=cls,
|
29
|
-
**kwargs
|
30
|
-
)
|
31
|
-
|
32
|
-
|
33
|
-
def Select(*options, cls: str = form_styles.select, **kwargs) -> ft.Select:
|
34
|
-
"""Flexible select component"""
|
35
|
-
return ft.Select(
|
36
|
-
*options,
|
37
|
-
cls=cls,
|
38
|
-
**kwargs
|
39
|
-
)
|
40
|
-
|
41
|
-
|
42
|
-
def Label(text: str, cls: str = form_styles.label, **kwargs) -> ft.Label:
|
43
|
-
"""Flexible label component"""
|
44
|
-
return ft.Label(
|
45
|
-
text,
|
46
|
-
cls=cls,
|
47
|
-
**kwargs
|
48
|
-
)
|
49
|
-
|
50
|
-
|
51
|
-
def FormGroup(*content, cls: str = form_styles.form_group, **kwargs) -> ft.Div:
|
52
|
-
"""Form group container with default spacing"""
|
53
|
-
return ft.Div(
|
54
|
-
*content,
|
55
|
-
cls=cls,
|
56
|
-
**kwargs
|
57
|
-
)
|
eidos/components/typography.py
DELETED
@@ -1,85 +0,0 @@
|
|
1
|
-
"""Semantic typography components for EidosUI"""
|
2
|
-
|
3
|
-
import fastapi_tags as ft
|
4
|
-
from ..core.styles import typography_styles
|
5
|
-
from ..core.utils import merge_classes
|
6
|
-
|
7
|
-
|
8
|
-
def H1(*content, cls: str = typography_styles.h1, **kwargs) -> ft.H1:
|
9
|
-
"""Semantic H1 heading"""
|
10
|
-
return ft.H1(*content, cls=cls, **kwargs)
|
11
|
-
|
12
|
-
|
13
|
-
def H2(*content, cls: str = typography_styles.h2, **kwargs) -> ft.H2:
|
14
|
-
"""Semantic H2 heading"""
|
15
|
-
return ft.H2(*content, cls=cls, **kwargs)
|
16
|
-
|
17
|
-
|
18
|
-
def H3(*content, cls: str = typography_styles.h3, **kwargs) -> ft.H3:
|
19
|
-
"""Semantic H3 heading"""
|
20
|
-
return ft.H3(*content, cls=cls, **kwargs)
|
21
|
-
|
22
|
-
|
23
|
-
def H4(*content, cls: str = typography_styles.h4, **kwargs) -> ft.H4:
|
24
|
-
"""Semantic H4 heading"""
|
25
|
-
return ft.H4(*content, cls=cls, **kwargs)
|
26
|
-
|
27
|
-
|
28
|
-
def H5(*content, cls: str = typography_styles.h5, **kwargs) -> ft.H5:
|
29
|
-
"""Semantic H5 heading"""
|
30
|
-
return ft.H5(*content, cls=cls, **kwargs)
|
31
|
-
|
32
|
-
|
33
|
-
def H6(*content, cls: str = typography_styles.h6, **kwargs) -> ft.H6:
|
34
|
-
"""Semantic H6 heading"""
|
35
|
-
return ft.H6(*content, cls=cls, **kwargs)
|
36
|
-
|
37
|
-
|
38
|
-
def P(*content, cls: str = typography_styles.body, **kwargs) -> ft.P:
|
39
|
-
"""Semantic paragraph"""
|
40
|
-
return ft.P(*content, cls=cls, **kwargs)
|
41
|
-
|
42
|
-
|
43
|
-
def Text(*content, cls: str = typography_styles.body, **kwargs) -> ft.Span:
|
44
|
-
"""Generic text span"""
|
45
|
-
return ft.Span(*content, cls=cls, **kwargs)
|
46
|
-
|
47
|
-
|
48
|
-
def Em(*content, cls: str = typography_styles.em, **kwargs) -> ft.Em:
|
49
|
-
"""Semantic emphasis (italic)"""
|
50
|
-
return ft.Em(*content, cls=cls, **kwargs)
|
51
|
-
|
52
|
-
|
53
|
-
def Strong(*content, cls: str = typography_styles.strong, **kwargs) -> ft.Strong:
|
54
|
-
"""Semantic strong emphasis (bold)"""
|
55
|
-
return ft.Strong(*content, cls=cls, **kwargs)
|
56
|
-
|
57
|
-
|
58
|
-
def A(*content, href: str = "#", cls: str = typography_styles.link, **kwargs) -> ft.A:
|
59
|
-
"""Semantic anchor link"""
|
60
|
-
return ft.A(*content, href=href, cls=cls, **kwargs)
|
61
|
-
|
62
|
-
|
63
|
-
def Code(*content, cls: str = typography_styles.code, **kwargs) -> ft.Code:
|
64
|
-
"""Inline code"""
|
65
|
-
return ft.Code(*content, cls=cls, **kwargs)
|
66
|
-
|
67
|
-
|
68
|
-
def Pre(*content, cls: str = typography_styles.pre, **kwargs) -> ft.Pre:
|
69
|
-
"""Preformatted text block"""
|
70
|
-
return ft.Pre(*content, cls=cls, **kwargs)
|
71
|
-
|
72
|
-
|
73
|
-
# def Blockquote(*content, cls: str = typography_styles.blockquote, **kwargs) -> ft.Blockquote:
|
74
|
-
# """Semantic blockquote"""
|
75
|
-
# return ft.Blockquote(*content, cls=cls, **kwargs)
|
76
|
-
|
77
|
-
|
78
|
-
def Mark(*content, cls: str = typography_styles.mark, **kwargs) -> ft.Mark:
|
79
|
-
"""Highlighted text"""
|
80
|
-
return ft.Mark(*content, cls=cls, **kwargs)
|
81
|
-
|
82
|
-
|
83
|
-
def Small(*content, cls: str = typography_styles.small, **kwargs) -> ft.Small:
|
84
|
-
"""Small text"""
|
85
|
-
return ft.Small(*content, cls=cls, **kwargs)
|
eidos/core/__init__.py
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
"""Core functionality for EidosUI theme and style system"""
|
2
|
-
|
3
|
-
from .styles import (
|
4
|
-
button_styles,
|
5
|
-
typography_styles,
|
6
|
-
form_styles,
|
7
|
-
)
|
8
|
-
from .utils import merge_classes
|
9
|
-
from .helpers import (
|
10
|
-
serve_eidos_static,
|
11
|
-
create_eidos_head_tag,
|
12
|
-
get_theme_css,
|
13
|
-
get_eidos_js,
|
14
|
-
)
|
15
|
-
|
16
|
-
__all__ = [
|
17
|
-
"button_styles",
|
18
|
-
"typography_styles",
|
19
|
-
"form_styles",
|
20
|
-
"merge_classes",
|
21
|
-
"serve_eidos_static",
|
22
|
-
"create_eidos_head_tag",
|
23
|
-
"get_theme_css",
|
24
|
-
"get_eidos_js",
|
25
|
-
]
|
eidos/core/helpers.py
DELETED
@@ -1,87 +0,0 @@
|
|
1
|
-
"""Helper functions for easy EidosUI integration"""
|
2
|
-
|
3
|
-
import importlib.resources
|
4
|
-
from pathlib import Path
|
5
|
-
from typing import Optional
|
6
|
-
from fastapi import FastAPI
|
7
|
-
from fastapi.staticfiles import StaticFiles
|
8
|
-
|
9
|
-
|
10
|
-
def get_theme_css(theme: str = "light") -> str:
|
11
|
-
"""Get CSS content for a theme directly from the package"""
|
12
|
-
try:
|
13
|
-
with importlib.resources.open_text("eidos.themes", f"{theme}.css") as f:
|
14
|
-
return f.read()
|
15
|
-
except FileNotFoundError:
|
16
|
-
return ""
|
17
|
-
|
18
|
-
|
19
|
-
def get_eidos_js() -> str:
|
20
|
-
"""Get JavaScript content directly from the package"""
|
21
|
-
try:
|
22
|
-
with importlib.resources.open_text("eidos.static", "eidos-ui.js") as f:
|
23
|
-
return f.read()
|
24
|
-
except FileNotFoundError:
|
25
|
-
return ""
|
26
|
-
|
27
|
-
def serve_eidos_static(app: FastAPI, prefix: str = "/eidos") -> None:
|
28
|
-
"""
|
29
|
-
Automatically mount EidosUI static files to a FastAPI app
|
30
|
-
|
31
|
-
Args:
|
32
|
-
app: FastAPI application instance
|
33
|
-
prefix: URL prefix for static files (default: "/eidos")
|
34
|
-
"""
|
35
|
-
try:
|
36
|
-
# Get the package directory paths
|
37
|
-
with importlib.resources.path("eidos", "static") as static_path:
|
38
|
-
app.mount(f"{prefix}/static", StaticFiles(directory=str(static_path)), name="eidos_static")
|
39
|
-
|
40
|
-
with importlib.resources.path("eidos", "themes") as themes_path:
|
41
|
-
app.mount(f"{prefix}/themes", StaticFiles(directory=str(themes_path)), name="eidos_themes")
|
42
|
-
|
43
|
-
except Exception as e:
|
44
|
-
# Fallback for development - try relative paths
|
45
|
-
import os
|
46
|
-
package_dir = Path(__file__).parent.parent
|
47
|
-
|
48
|
-
if (package_dir / "static").exists():
|
49
|
-
app.mount(f"{prefix}/static", StaticFiles(directory=str(package_dir / "static")), name="eidos_static")
|
50
|
-
|
51
|
-
if (package_dir / "themes").exists():
|
52
|
-
app.mount(f"{prefix}/themes", StaticFiles(directory=str(package_dir / "themes")), name="eidos_themes")
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
def create_eidos_head_tag(
|
59
|
-
title: str = "EidosUI App",
|
60
|
-
themes: list = ["light", "dark"],
|
61
|
-
prefix: str = "/eidos",
|
62
|
-
include_tailwind: bool = True
|
63
|
-
):
|
64
|
-
"""Create a fastapi-tags Head component with EidosUI setup"""
|
65
|
-
import fastapi_tags as ft
|
66
|
-
|
67
|
-
head_content = []
|
68
|
-
|
69
|
-
head_content.extend([
|
70
|
-
ft.Meta(charset="UTF-8"),
|
71
|
-
ft.Meta(name="viewport", content="width=device-width, initial-scale=1.0"),
|
72
|
-
ft.Title(title)
|
73
|
-
])
|
74
|
-
|
75
|
-
if include_tailwind:
|
76
|
-
head_content.append(ft.Script(src="https://cdn.tailwindcss.com"))
|
77
|
-
|
78
|
-
# Add CSS links
|
79
|
-
for theme in themes:
|
80
|
-
head_content.append(ft.Link(rel="stylesheet", href=f"{prefix}/themes/{theme}.css"))
|
81
|
-
|
82
|
-
# Add JS script
|
83
|
-
head_content.append(ft.Script(src=f"{prefix}/static/eidos-ui.js"))
|
84
|
-
|
85
|
-
return ft.Head(*head_content)
|
86
|
-
|
87
|
-
|
eidos/core/styles.py
DELETED
@@ -1,92 +0,0 @@
|
|
1
|
-
"""Style dataclasses for EidosUI components using CSS variables"""
|
2
|
-
|
3
|
-
from dataclasses import dataclass
|
4
|
-
|
5
|
-
|
6
|
-
@dataclass(frozen=True)
|
7
|
-
class ButtonStyles:
|
8
|
-
"""Button style variations using CSS variables"""
|
9
|
-
|
10
|
-
# Primary styles (most commonly used)
|
11
|
-
primary: str = "bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-[var(--color-primary-foreground)] font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:ring-offset-2 shadow-sm hover:shadow-md active:scale-95"
|
12
|
-
secondary: str = "bg-[var(--color-secondary)] hover:bg-[var(--color-secondary-hover)] text-[var(--color-secondary-foreground)] font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[var(--color-secondary)] focus:ring-offset-2 shadow-sm hover:shadow-md active:scale-95"
|
13
|
-
ghost: str = "text-[var(--color-primary)] hover:bg-[var(--color-primary-light)] font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:ring-offset-2 active:scale-95"
|
14
|
-
|
15
|
-
# Semantic styles
|
16
|
-
success: str = "bg-[var(--color-success)] hover:bg-[var(--color-success-hover)] text-[var(--color-success-foreground)] font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[var(--color-success)] focus:ring-offset-2 shadow-sm hover:shadow-md active:scale-95"
|
17
|
-
cta: str = "bg-[var(--color-cta)] hover:bg-[var(--color-cta-hover)] text-[var(--color-cta-foreground)] font-semibold rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[var(--color-cta)] focus:ring-offset-2 shadow-md hover:shadow-lg active:scale-95 transform"
|
18
|
-
warning: str = "bg-[var(--color-warning)] hover:bg-[var(--color-warning-hover)] text-[var(--color-warning-foreground)] font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[var(--color-warning)] focus:ring-offset-2 shadow-sm hover:shadow-md active:scale-95"
|
19
|
-
error: str = "bg-[var(--color-error)] hover:bg-[var(--color-error-hover)] text-[var(--color-error-foreground)] font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[var(--color-error)] focus:ring-offset-2 shadow-sm hover:shadow-md active:scale-95"
|
20
|
-
info: str = "bg-[var(--color-info)] hover:bg-[var(--color-info-hover)] text-[var(--color-info-foreground)] font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[var(--color-info)] focus:ring-offset-2 shadow-sm hover:shadow-md active:scale-95"
|
21
|
-
|
22
|
-
# Size variations
|
23
|
-
sm: str = "px-3 py-1.5 text-sm"
|
24
|
-
md: str = "px-4 py-2 text-base" # Default size
|
25
|
-
lg: str = "px-6 py-3 text-lg"
|
26
|
-
xl: str = "px-8 py-4 text-xl"
|
27
|
-
|
28
|
-
# Icon styles
|
29
|
-
icon_sm: str = "p-1.5"
|
30
|
-
icon_md: str = "p-2"
|
31
|
-
icon_lg: str = "p-3"
|
32
|
-
|
33
|
-
# Special styles
|
34
|
-
outline_primary: str = "border-2 border-[var(--color-primary)] text-[var(--color-primary)] hover:bg-[var(--color-primary)] hover:text-[var(--color-primary-foreground)] font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:ring-offset-2 active:scale-95"
|
35
|
-
outline_secondary: str = "border-2 border-[var(--color-secondary)] text-[var(--color-secondary)] hover:bg-[var(--color-secondary)] hover:text-[var(--color-secondary-foreground)] font-medium rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[var(--color-secondary)] focus:ring-offset-2 active:scale-95"
|
36
|
-
link: str = "text-[var(--color-primary)] hover:text-[var(--color-primary-hover)] underline-offset-4 hover:underline font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:ring-offset-2 rounded active:scale-95"
|
37
|
-
|
38
|
-
|
39
|
-
@dataclass(frozen=True)
|
40
|
-
class TypographyStyles:
|
41
|
-
"""Typography style variations using CSS variables"""
|
42
|
-
|
43
|
-
# Heading styles (mobile-first responsive)
|
44
|
-
h1: str = "text-2xl sm:text-3xl lg:text-4xl font-bold text-[var(--color-text)] leading-tight"
|
45
|
-
h2: str = "text-xl sm:text-2xl lg:text-3xl font-semibold text-[var(--color-text)] leading-tight"
|
46
|
-
h3: str = "text-lg sm:text-xl lg:text-2xl font-semibold text-[var(--color-text)] leading-tight"
|
47
|
-
h4: str = "text-base sm:text-lg lg:text-xl font-semibold text-[var(--color-text)] leading-tight"
|
48
|
-
h5: str = "text-sm sm:text-base lg:text-lg font-medium text-[var(--color-text)] leading-tight"
|
49
|
-
h6: str = "text-xs sm:text-sm lg:text-base font-medium text-[var(--color-text)] leading-tight"
|
50
|
-
|
51
|
-
# Body text styles
|
52
|
-
body: str = "text-base text-[var(--color-text)] leading-relaxed"
|
53
|
-
|
54
|
-
# Semantic emphasis
|
55
|
-
em: str = "italic"
|
56
|
-
strong: str = "font-semibold"
|
57
|
-
small: str = "text-sm text-[var(--color-text-muted)]"
|
58
|
-
|
59
|
-
# Links
|
60
|
-
link: str = "text-[var(--color-primary)] hover:text-[var(--color-primary-hover)] underline underline-offset-2 transition-colors duration-200"
|
61
|
-
|
62
|
-
# Text decorations
|
63
|
-
code: str = "font-mono bg-[var(--color-surface)] px-1.5 py-0.5 rounded text-sm"
|
64
|
-
pre: str = "bg-[var(--color-surface)] border border-[var(--color-border)] rounded-lg p-4 overflow-x-auto text-sm font-mono"
|
65
|
-
mark: str = "bg-[var(--color-warning-light)] px-1 rounded"
|
66
|
-
blockquote: str = "border-l-4 border-[var(--color-primary)] pl-6 py-2 italic text-[var(--color-text-muted)]"
|
67
|
-
|
68
|
-
|
69
|
-
@dataclass(frozen=True)
|
70
|
-
class FormStyles:
|
71
|
-
"""Form component styles using CSS variables"""
|
72
|
-
|
73
|
-
# Input styles
|
74
|
-
input: str = "w-full px-3 py-2 bg-[var(--color-input)] border border-[var(--color-border)] rounded-lg text-[var(--color-text)] placeholder:text-[var(--color-text-muted)] focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:border-transparent transition-colors duration-200"
|
75
|
-
|
76
|
-
# Textarea
|
77
|
-
textarea: str = "w-full px-3 py-2 bg-[var(--color-input)] border border-[var(--color-border)] rounded-lg text-[var(--color-text)] placeholder:text-[var(--color-text-muted)] focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:border-transparent transition-colors duration-200 resize-y min-h-[80px]"
|
78
|
-
|
79
|
-
# Select
|
80
|
-
select: str = "w-full px-3 py-2 bg-[var(--color-input)] border border-[var(--color-border)] rounded-lg text-[var(--color-text)] focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:border-transparent transition-colors duration-200 appearance-none"
|
81
|
-
|
82
|
-
# Labels
|
83
|
-
label: str = "block text-sm font-medium text-[var(--color-text)] mb-1"
|
84
|
-
|
85
|
-
# Form groups
|
86
|
-
form_group: str = "space-y-2"
|
87
|
-
|
88
|
-
|
89
|
-
# Global style instances for easy access
|
90
|
-
button_styles = ButtonStyles()
|
91
|
-
typography_styles = TypographyStyles()
|
92
|
-
form_styles = FormStyles()
|
eidos/core/utils.py
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
"""Utility functions for EidosUI"""
|
2
|
-
|
3
|
-
from typing import Union, Optional, List
|
4
|
-
|
5
|
-
|
6
|
-
def merge_classes(*classes: Optional[Union[str, List[str]]]) -> str:
|
7
|
-
"""
|
8
|
-
Merge multiple class strings, handling None values and lists
|
9
|
-
|
10
|
-
Args:
|
11
|
-
*classes: Variable number of class strings, lists of strings, or None values
|
12
|
-
|
13
|
-
Returns:
|
14
|
-
A single merged class string with duplicates removed and proper spacing
|
15
|
-
|
16
|
-
Examples:
|
17
|
-
>>> merge_classes("text-base", "font-bold", None, "text-center")
|
18
|
-
"text-base font-bold text-center"
|
19
|
-
|
20
|
-
>>> merge_classes(["bg-blue-500", "hover:bg-blue-600"], "rounded-lg")
|
21
|
-
"bg-blue-500 hover:bg-blue-600 rounded-lg"
|
22
|
-
"""
|
23
|
-
result = []
|
24
|
-
|
25
|
-
for cls in classes:
|
26
|
-
if cls is None:
|
27
|
-
continue
|
28
|
-
|
29
|
-
if isinstance(cls, (list, tuple)):
|
30
|
-
# Handle lists/tuples of classes
|
31
|
-
for item in cls:
|
32
|
-
if item and isinstance(item, str):
|
33
|
-
result.extend(item.split())
|
34
|
-
elif isinstance(cls, str) and cls.strip():
|
35
|
-
# Handle string classes
|
36
|
-
result.extend(cls.split())
|
37
|
-
|
38
|
-
# Remove duplicates while preserving order
|
39
|
-
seen = set()
|
40
|
-
unique_classes = []
|
41
|
-
for class_name in result:
|
42
|
-
if class_name not in seen:
|
43
|
-
seen.add(class_name)
|
44
|
-
unique_classes.append(class_name)
|
45
|
-
|
46
|
-
return ' '.join(unique_classes)
|
eidos/static/eidos-ui.js
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* EidosUI Theme Switcher
|
3
|
-
* Client-side utilities for theme management
|
4
|
-
*/
|
5
|
-
|
6
|
-
(function() {
|
7
|
-
'use strict';
|
8
|
-
|
9
|
-
// Theme management functions
|
10
|
-
window.EidosUI = {
|
11
|
-
/**
|
12
|
-
* Set the current theme
|
13
|
-
* @param {string} theme - Theme name ('light' or 'dark')
|
14
|
-
*/
|
15
|
-
setTheme: function(theme) {
|
16
|
-
document.documentElement.setAttribute('data-theme', theme);
|
17
|
-
localStorage.setItem('eidos-theme', theme);
|
18
|
-
|
19
|
-
// Dispatch custom event
|
20
|
-
document.dispatchEvent(new CustomEvent('eidos:theme-changed', {
|
21
|
-
detail: { theme: theme }
|
22
|
-
}));
|
23
|
-
},
|
24
|
-
|
25
|
-
/**
|
26
|
-
* Get the current theme
|
27
|
-
* @returns {string} Current theme name
|
28
|
-
*/
|
29
|
-
getTheme: function() {
|
30
|
-
return document.documentElement.getAttribute('data-theme') || 'light';
|
31
|
-
},
|
32
|
-
|
33
|
-
/**
|
34
|
-
* Toggle between light and dark themes
|
35
|
-
*/
|
36
|
-
toggleTheme: function() {
|
37
|
-
const current = this.getTheme();
|
38
|
-
const next = current === 'light' ? 'dark' : 'light';
|
39
|
-
this.setTheme(next);
|
40
|
-
},
|
41
|
-
|
42
|
-
/**
|
43
|
-
* Initialize theme from localStorage or system preference
|
44
|
-
*/
|
45
|
-
initTheme: function() {
|
46
|
-
const savedTheme = localStorage.getItem('eidos-theme');
|
47
|
-
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
48
|
-
const theme = savedTheme || systemTheme;
|
49
|
-
this.setTheme(theme);
|
50
|
-
}
|
51
|
-
};
|
52
|
-
|
53
|
-
// Auto-initialize when DOM is ready
|
54
|
-
if (document.readyState === 'loading') {
|
55
|
-
document.addEventListener('DOMContentLoaded', function() {
|
56
|
-
window.EidosUI.initTheme();
|
57
|
-
});
|
58
|
-
} else {
|
59
|
-
window.EidosUI.initTheme();
|
60
|
-
}
|
61
|
-
|
62
|
-
// Listen for system theme changes
|
63
|
-
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
|
64
|
-
// Only auto-switch if user hasn't set a preference
|
65
|
-
if (!localStorage.getItem('eidos-theme')) {
|
66
|
-
window.EidosUI.setTheme(e.matches ? 'dark' : 'light');
|
67
|
-
}
|
68
|
-
});
|
69
|
-
|
70
|
-
// Global functions for convenience
|
71
|
-
window.setTheme = window.EidosUI.setTheme.bind(window.EidosUI);
|
72
|
-
window.toggleTheme = window.EidosUI.toggleTheme.bind(window.EidosUI);
|
73
|
-
|
74
|
-
})();
|
eidos/themes/dark.css
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
/* EidosUI Dark Theme */
|
2
|
-
[data-theme="dark"] {
|
3
|
-
/* Surface Colors - Dark Mode Overrides */
|
4
|
-
--color-background: #0f172a;
|
5
|
-
--color-surface: #1e293b;
|
6
|
-
--color-surface-elevated: #334155;
|
7
|
-
--color-border: #334155;
|
8
|
-
--color-border-hover: #475569;
|
9
|
-
--color-input: #1e293b;
|
10
|
-
--color-card: #1e293b;
|
11
|
-
|
12
|
-
/* Text Colors - Dark Mode Overrides */
|
13
|
-
--color-text: #f8fafc;
|
14
|
-
--color-text-muted: #94a3b8;
|
15
|
-
--color-text-subtle: #64748b;
|
16
|
-
--color-text-inverse: #0f172a;
|
17
|
-
|
18
|
-
/* Primary Color Override - Blue for Dark Mode */
|
19
|
-
--color-primary: #60a5fa;
|
20
|
-
--color-primary-hover: #3b82f6;
|
21
|
-
--color-primary-light: #1e3a8a;
|
22
|
-
--color-primary-dark: #2563eb;
|
23
|
-
--color-primary-foreground: #1f2937;
|
24
|
-
|
25
|
-
/* Dark mode overrides for semantic colors */
|
26
|
-
--color-secondary: #9ca3af;
|
27
|
-
--color-secondary-hover: #d1d5db;
|
28
|
-
--color-secondary-light: #374151;
|
29
|
-
--color-secondary-foreground: #1f2937;
|
30
|
-
|
31
|
-
--color-success: #34d399;
|
32
|
-
--color-success-hover: #10b981;
|
33
|
-
--color-success-light: #065f46;
|
34
|
-
|
35
|
-
--color-cta: #fbbf24;
|
36
|
-
--color-cta-hover: #f59e0b;
|
37
|
-
--color-cta-light: #92400e;
|
38
|
-
--color-cta-foreground: #1f2937;
|
39
|
-
|
40
|
-
/* Other semantic colors for dark mode */
|
41
|
-
--color-accent-light: #581c87;
|
42
|
-
--color-warning-light: #713f12;
|
43
|
-
--color-error-light: #7f1d1d;
|
44
|
-
--color-info-light: #0c4a6e;
|
45
|
-
|
46
|
-
/* Enhanced shadows for dark mode */
|
47
|
-
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
|
48
|
-
--shadow-base: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.4);
|
49
|
-
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.4);
|
50
|
-
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.4), 0 4px 6px -4px rgb(0 0 0 / 0.4);
|
51
|
-
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.4), 0 8px 10px -6px rgb(0 0 0 / 0.4);
|
52
|
-
}
|
eidos/themes/light.css
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
/* EidosUI Light Theme */
|
2
|
-
:root {
|
3
|
-
/* Core Colors */
|
4
|
-
--color-primary: #3b82f6;
|
5
|
-
--color-primary-hover: #2563eb;
|
6
|
-
--color-primary-light: #dbeafe;
|
7
|
-
--color-primary-dark: #1d4ed8;
|
8
|
-
--color-primary-foreground: #ffffff;
|
9
|
-
|
10
|
-
--color-secondary: #6b7280;
|
11
|
-
--color-secondary-hover: #4b5563;
|
12
|
-
--color-secondary-light: #f9fafb;
|
13
|
-
--color-secondary-dark: #374151;
|
14
|
-
--color-secondary-foreground: #ffffff;
|
15
|
-
|
16
|
-
--color-accent: #7c3aed;
|
17
|
-
--color-accent-hover: #6d28d9;
|
18
|
-
--color-accent-light: #f3e8ff;
|
19
|
-
--color-accent-foreground: #ffffff;
|
20
|
-
|
21
|
-
/* Semantic Colors */
|
22
|
-
--color-success: #10b981;
|
23
|
-
--color-success-hover: #059669;
|
24
|
-
--color-success-light: #d1fae5;
|
25
|
-
--color-success-foreground: #ffffff;
|
26
|
-
|
27
|
-
--color-cta: #f59e0b;
|
28
|
-
--color-cta-hover: #d97706;
|
29
|
-
--color-cta-light: #fef3c7;
|
30
|
-
--color-cta-foreground: #ffffff;
|
31
|
-
|
32
|
-
--color-warning: #eab308;
|
33
|
-
--color-warning-hover: #ca8a04;
|
34
|
-
--color-warning-light: #fefce8;
|
35
|
-
--color-warning-foreground: #000000;
|
36
|
-
|
37
|
-
--color-error: #dc2626;
|
38
|
-
--color-error-hover: #b91c1c;
|
39
|
-
--color-error-light: #fef2f2;
|
40
|
-
--color-error-foreground: #ffffff;
|
41
|
-
|
42
|
-
--color-info: #0ea5e9;
|
43
|
-
--color-info-hover: #0284c7;
|
44
|
-
--color-info-light: #f0f9ff;
|
45
|
-
--color-info-foreground: #ffffff;
|
46
|
-
|
47
|
-
/* Surface Colors */
|
48
|
-
--color-background: #ffffff;
|
49
|
-
--color-surface: #f8fafc;
|
50
|
-
--color-surface-elevated: #ffffff;
|
51
|
-
--color-border: #e2e8f0;
|
52
|
-
--color-border-hover: #cbd5e1;
|
53
|
-
--color-input: #ffffff;
|
54
|
-
--color-card: #ffffff;
|
55
|
-
|
56
|
-
/* Text Colors */
|
57
|
-
--color-text: #0f172a;
|
58
|
-
--color-text-muted: #64748b;
|
59
|
-
--color-text-subtle: #94a3b8;
|
60
|
-
--color-text-inverse: #ffffff;
|
61
|
-
|
62
|
-
/* Spacing Scale */
|
63
|
-
--space-xs: 0.25rem; /* 4px */
|
64
|
-
--space-sm: 0.5rem; /* 8px */
|
65
|
-
--space-md: 1rem; /* 16px */
|
66
|
-
--space-lg: 1.5rem; /* 24px */
|
67
|
-
--space-xl: 2rem; /* 32px */
|
68
|
-
--space-2xl: 3rem; /* 48px */
|
69
|
-
--space-3xl: 4rem; /* 64px */
|
70
|
-
|
71
|
-
/* Typography Scale */
|
72
|
-
--font-size-xs: 0.75rem; /* 12px */
|
73
|
-
--font-size-sm: 0.875rem; /* 14px */
|
74
|
-
--font-size-base: 1rem; /* 16px */
|
75
|
-
--font-size-lg: 1.125rem; /* 18px */
|
76
|
-
--font-size-xl: 1.25rem; /* 20px */
|
77
|
-
--font-size-2xl: 1.5rem; /* 24px */
|
78
|
-
--font-size-3xl: 1.875rem; /* 30px */
|
79
|
-
--font-size-4xl: 2.25rem; /* 36px */
|
80
|
-
|
81
|
-
--font-weight-normal: 400;
|
82
|
-
--font-weight-medium: 500;
|
83
|
-
--font-weight-semibold: 600;
|
84
|
-
--font-weight-bold: 700;
|
85
|
-
|
86
|
-
--line-height-tight: 1.25;
|
87
|
-
--line-height-normal: 1.5;
|
88
|
-
--line-height-relaxed: 1.75;
|
89
|
-
|
90
|
-
/* Border Radius */
|
91
|
-
--radius-none: 0;
|
92
|
-
--radius-sm: 0.125rem; /* 2px */
|
93
|
-
--radius-base: 0.375rem; /* 6px */
|
94
|
-
--radius-md: 0.5rem; /* 8px */
|
95
|
-
--radius-lg: 0.75rem; /* 12px */
|
96
|
-
--radius-xl: 1rem; /* 16px */
|
97
|
-
--radius-full: 9999px;
|
98
|
-
|
99
|
-
/* Shadows */
|
100
|
-
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
101
|
-
--shadow-base: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
102
|
-
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
103
|
-
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
104
|
-
--shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
|
105
|
-
|
106
|
-
/* Animation */
|
107
|
-
--transition-fast: 150ms ease;
|
108
|
-
--transition-base: 200ms ease;
|
109
|
-
--transition-slow: 300ms ease;
|
110
|
-
}
|