eidosui 0.3.0__py3-none-any.whl → 0.5.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/tags.py CHANGED
@@ -1,99 +1,435 @@
1
- from typing import Optional, Literal, Any, Union
1
+ from typing import Any
2
+
2
3
  import air
4
+
3
5
  from . import styles
4
6
  from .utils import stringify
5
7
 
6
- def Button(*content: Any, class_: Optional[Union[str, list[str]]] = styles.buttons.primary, **kwargs: Any) -> air.Tag:
8
+ # Define exports for this module
9
+ __all__ = [
10
+ # Custom EidosUI components
11
+ "Button",
12
+ "H1",
13
+ "H2",
14
+ "H3",
15
+ "H4",
16
+ "H5",
17
+ "H6",
18
+ "Body",
19
+ # Semantic components with styling
20
+ "Strong",
21
+ "I",
22
+ "Small",
23
+ "Del",
24
+ "Abbr",
25
+ "Var",
26
+ "Mark",
27
+ "Time",
28
+ "Code",
29
+ "Pre",
30
+ "Kbd",
31
+ "Samp",
32
+ "Blockquote",
33
+ "Cite",
34
+ "Address",
35
+ "Hr",
36
+ "Details",
37
+ "Summary",
38
+ "Dl",
39
+ "Dt",
40
+ "Dd",
41
+ "Figure",
42
+ "Figcaption",
43
+ # Table components with styling
44
+ "Table",
45
+ "Thead",
46
+ "Tbody",
47
+ "Tfoot",
48
+ "Tr",
49
+ "Th",
50
+ "Td",
51
+ # Pass-through HTML tags from air.tags
52
+ "A",
53
+ "Area",
54
+ "Article",
55
+ "Aside",
56
+ "Audio",
57
+ "B",
58
+ "Base",
59
+ "Bdi",
60
+ "Bdo",
61
+ "Br",
62
+ "Canvas",
63
+ "Caption",
64
+ "Col",
65
+ "Colgroup",
66
+ "Data",
67
+ "Datalist",
68
+ "Dfn",
69
+ "Dialog",
70
+ "Div",
71
+ "Em",
72
+ "Embed",
73
+ "Fieldset",
74
+ "Footer",
75
+ "Form",
76
+ "Head",
77
+ "Header",
78
+ "Hgroup",
79
+ "Html",
80
+ "Iframe",
81
+ "Img",
82
+ "Input",
83
+ "Ins",
84
+ "Label",
85
+ "Legend",
86
+ "Li",
87
+ "Link",
88
+ "Main",
89
+ "Map",
90
+ "Menu",
91
+ "Meta",
92
+ "Meter",
93
+ "Nav",
94
+ "Noscript",
95
+ "Object",
96
+ "Ol",
97
+ "Optgroup",
98
+ "Option",
99
+ "Output",
100
+ "P",
101
+ "Param",
102
+ "Picture",
103
+ "Progress",
104
+ "Q",
105
+ "Rp",
106
+ "Rt",
107
+ "Ruby",
108
+ "S",
109
+ "Script",
110
+ "Search",
111
+ "Section",
112
+ "Select",
113
+ "Source",
114
+ "Span",
115
+ "Style",
116
+ "Sub",
117
+ "Sup",
118
+ "Template",
119
+ "Textarea",
120
+ "Title",
121
+ "Track",
122
+ "U",
123
+ "Ul",
124
+ "Video",
125
+ "Wbr",
126
+ ]
127
+
128
+
129
+ def Button(
130
+ *content: Any,
131
+ class_: str | list[str] | None = styles.buttons.primary,
132
+ **kwargs: Any,
133
+ ) -> air.Tag:
134
+ """
135
+ Args:
136
+ content: The content of the button.
137
+ class_: The class of the button.
138
+ **kwargs: Additional keyword arguments passed to the button tag.
139
+
140
+ Returns:
141
+ air.Tag: The button tag.
142
+
143
+ Example:
144
+ Button("Click me", class_=styles.buttons.primary)
145
+ """
7
146
  return air.Button(*content, class_=stringify(styles.buttons.base, class_), **kwargs)
8
147
 
9
- def H1(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
148
+
149
+ def H1(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
150
+ """
151
+ Args:
152
+ content: The content of the h1 tag.
153
+ class_: The class of the h1 tag.
154
+ **kwargs: Additional keyword arguments passed to the h1 tag.
155
+
156
+ Returns:
157
+ air.Tag: The h1 tag.
158
+
159
+ Example:
160
+ H1("Hello, world!")
161
+ """
10
162
  return air.H1(*content, class_=stringify(styles.typography.h1, class_), **kwargs)
11
163
 
12
- def H2(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
164
+
165
+ def H2(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
166
+ """
167
+ Args:
168
+ content: The content of the h2 tag.
169
+ class_: The class of the h2 tag.
170
+ **kwargs: Additional keyword arguments passed to the h2 tag.
171
+
172
+ Returns:
173
+ air.Tag: The h2 tag.
174
+
175
+ Example:
176
+ H2("Hello, world!")
177
+ """
13
178
  return air.H2(*content, class_=stringify(styles.typography.h2, class_), **kwargs)
14
179
 
15
- def H3(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
180
+
181
+ def H3(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
182
+ """
183
+ Args:
184
+ content: The content of the h3 tag.
185
+ class_: The class of the h3 tag.
186
+ **kwargs: Additional keyword arguments passed to the h3 tag.
187
+
188
+ Returns:
189
+ air.Tag: The h3 tag.
190
+
191
+ Example:
192
+ H3("Hello, world!")
193
+ """
16
194
  return air.H3(*content, class_=stringify(styles.typography.h3, class_), **kwargs)
17
195
 
18
- def H4(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
196
+
197
+ def H4(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
19
198
  return air.H4(*content, class_=stringify(styles.typography.h4, class_), **kwargs)
20
199
 
21
- def H5(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
200
+
201
+ def H5(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
22
202
  return air.H5(*content, class_=stringify(styles.typography.h5, class_), **kwargs)
23
203
 
24
- def H6(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
204
+
205
+ def H6(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
25
206
  return air.H6(*content, class_=stringify(styles.typography.h6, class_), **kwargs)
26
207
 
27
- def Body(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
208
+
209
+ def Body(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
28
210
  return air.Body(*content, class_=stringify(styles.Theme.body, class_), **kwargs)
29
211
 
212
+
30
213
  # Semantic HTML Elements
31
214
 
32
- def Strong(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
215
+
216
+ def Strong(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
33
217
  return air.Strong(*content, class_=stringify(styles.semantic.strong, class_), **kwargs)
34
218
 
35
- def I(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
219
+
220
+ def I(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
36
221
  return air.I(*content, class_=stringify(styles.semantic.i, class_), **kwargs)
37
222
 
38
- def Small(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
223
+
224
+ def Small(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
39
225
  return air.Small(*content, class_=stringify(styles.semantic.small, class_), **kwargs)
40
226
 
41
- def Del(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
227
+
228
+ def Del(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
42
229
  return air.Del(*content, class_=stringify(styles.semantic.del_, class_), **kwargs)
43
230
 
44
- def Abbr(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
231
+
232
+ def Abbr(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
233
+ """
234
+ Args:
235
+ content: The content of the abbr tag.
236
+ class_: The class of the abbr tag.
237
+ **kwargs: Additional keyword arguments passed to the abbr tag.
238
+
239
+ Returns:
240
+ air.Tag: The abbr tag.
241
+
242
+ Example:
243
+ Abbr("HTML", title="Hyper Text Markup Language")
244
+ """
45
245
  return air.Abbr(*content, class_=stringify(styles.semantic.abbr, class_), **kwargs)
46
246
 
47
- def Var(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
247
+
248
+ def Var(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
48
249
  return air.Var(*content, class_=stringify(styles.semantic.var, class_), **kwargs)
49
250
 
50
- def Mark(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
251
+
252
+ def Mark(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
51
253
  return air.Mark(*content, class_=stringify(styles.semantic.mark, class_), **kwargs)
52
254
 
53
- def Time(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
255
+
256
+ def Time(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
54
257
  return air.Time(*content, class_=stringify(styles.semantic.time, class_), **kwargs)
55
258
 
56
- def Code(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
259
+
260
+ def Code(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
57
261
  return air.Code(*content, class_=stringify(styles.semantic.code, class_), **kwargs)
58
262
 
59
- def Pre(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
263
+
264
+ def Pre(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
60
265
  return air.Pre(*content, class_=stringify(styles.semantic.pre, class_), **kwargs)
61
266
 
62
- def Kbd(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
267
+
268
+ def Kbd(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
63
269
  return air.Kbd(*content, class_=stringify(styles.semantic.kbd, class_), **kwargs)
64
270
 
65
- def Samp(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
271
+
272
+ def Samp(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
66
273
  return air.Samp(*content, class_=stringify(styles.semantic.samp, class_), **kwargs)
67
274
 
68
- def Blockquote(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
275
+
276
+ def Blockquote(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
69
277
  return air.Blockquote(*content, class_=stringify(styles.semantic.blockquote, class_), **kwargs)
70
278
 
71
- def Cite(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
279
+
280
+ def Cite(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
72
281
  return air.Cite(*content, class_=stringify(styles.semantic.cite, class_), **kwargs)
73
282
 
74
- def Address(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
283
+
284
+ def Address(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
75
285
  return air.Address(*content, class_=stringify(styles.semantic.address, class_), **kwargs)
76
286
 
77
- def Hr(class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
287
+
288
+ def Hr(class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
78
289
  return air.Hr(class_=stringify(styles.semantic.hr, class_), **kwargs)
79
290
 
80
- def Details(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
291
+
292
+ def Details(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
81
293
  return air.Details(*content, class_=stringify(styles.semantic.details, class_), **kwargs)
82
294
 
83
- def Summary(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
295
+
296
+ def Summary(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
84
297
  return air.Summary(*content, class_=stringify(styles.semantic.summary, class_), **kwargs)
85
298
 
86
- def Dl(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
299
+
300
+ def Dl(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
87
301
  return air.Dl(*content, class_=stringify(styles.semantic.dl, class_), **kwargs)
88
302
 
89
- def Dt(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
303
+
304
+ def Dt(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
90
305
  return air.Dt(*content, class_=stringify(styles.semantic.dt, class_), **kwargs)
91
306
 
92
- def Dd(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
307
+
308
+ def Dd(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
93
309
  return air.Dd(*content, class_=stringify(styles.semantic.dd, class_), **kwargs)
94
310
 
95
- def Figure(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
311
+
312
+ def Figure(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
96
313
  return air.Figure(*content, class_=stringify(styles.semantic.figure, class_), **kwargs)
97
314
 
98
- def Figcaption(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
99
- return air.Figcaption(*content, class_=stringify(styles.semantic.figcaption, class_), **kwargs)
315
+
316
+ def Figcaption(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
317
+ return air.Figcaption(*content, class_=stringify(styles.semantic.figcaption, class_), **kwargs)
318
+
319
+
320
+ # Table elements with styling
321
+
322
+
323
+ def Table(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
324
+ """Styled table element."""
325
+ return air.Table(*content, class_=stringify(styles.tables.table, class_), **kwargs)
326
+
327
+
328
+ def Thead(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
329
+ """Styled table head element."""
330
+ return air.Thead(*content, class_=stringify(styles.tables.thead, class_), **kwargs)
331
+
332
+
333
+ def Tbody(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
334
+ """Styled table body element."""
335
+ return air.Tbody(*content, class_=stringify(styles.tables.tbody, class_), **kwargs)
336
+
337
+
338
+ def Tfoot(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
339
+ """Styled table footer element."""
340
+ return air.Tfoot(*content, class_=stringify(styles.tables.tfoot, class_), **kwargs)
341
+
342
+
343
+ def Tr(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
344
+ """Styled table row element."""
345
+ return air.Tr(*content, class_=stringify(styles.tables.tr, class_), **kwargs)
346
+
347
+
348
+ def Th(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
349
+ """Styled table header cell element."""
350
+ return air.Th(*content, class_=stringify(styles.tables.th, class_), **kwargs)
351
+
352
+
353
+ def Td(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
354
+ """Styled table data cell element."""
355
+ return air.Td(*content, class_=stringify(styles.tables.td, class_), **kwargs)
356
+
357
+
358
+ # Pass-through tags from air.tags
359
+ # Import all standard HTML tags that don't have custom styling
360
+ from air.tags import (
361
+ A,
362
+ Area,
363
+ Article,
364
+ Aside,
365
+ Audio,
366
+ B,
367
+ Base,
368
+ Bdi,
369
+ Bdo,
370
+ Br,
371
+ Canvas,
372
+ Caption,
373
+ Col,
374
+ Colgroup,
375
+ Data,
376
+ Datalist,
377
+ Dfn,
378
+ Dialog,
379
+ Div,
380
+ Em,
381
+ Embed,
382
+ Fieldset,
383
+ Footer,
384
+ Form,
385
+ Head,
386
+ Header,
387
+ Hgroup,
388
+ Html,
389
+ Iframe,
390
+ Img,
391
+ Input,
392
+ Ins,
393
+ Label,
394
+ Legend,
395
+ Li,
396
+ Link,
397
+ Main,
398
+ Map,
399
+ Menu,
400
+ Meta,
401
+ Meter,
402
+ Nav,
403
+ Noscript,
404
+ Object,
405
+ Ol,
406
+ Optgroup,
407
+ Option,
408
+ Output,
409
+ P,
410
+ Param,
411
+ Picture,
412
+ Progress,
413
+ Q,
414
+ Rp,
415
+ Rt,
416
+ Ruby,
417
+ S,
418
+ Script,
419
+ Search,
420
+ Section,
421
+ Select,
422
+ Source,
423
+ Span,
424
+ Style,
425
+ Sub,
426
+ Sup,
427
+ Template,
428
+ Textarea,
429
+ Title,
430
+ Track,
431
+ U,
432
+ Ul,
433
+ Video,
434
+ Wbr,
435
+ )
eidos/utils.py CHANGED
@@ -1,32 +1,30 @@
1
1
  """Core utility functions for EidosUI."""
2
2
 
3
- from typing import Optional, Union, List
4
- import os
5
- import sys
3
+ from pathlib import Path
6
4
 
7
5
 
8
- def stringify(*classes: Optional[Union[str, List[str]]]) -> str:
6
+ def stringify(*classes: str | list[str] | None) -> str:
9
7
  """
10
8
  Concatenate CSS classes, filtering out None values and flattening lists.
11
-
9
+
12
10
  Args:
13
11
  *classes: Variable number of class strings, lists of strings, or None values
14
-
12
+
15
13
  Returns:
16
14
  A single space-separated string of CSS classes
17
-
15
+
18
16
  Examples:
19
17
  >>> stringify("btn", "btn-primary")
20
18
  "btn btn-primary"
21
-
19
+
22
20
  >>> stringify("btn", None, "btn-lg")
23
21
  "btn btn-lg"
24
-
22
+
25
23
  >>> stringify(["btn", "btn-primary"], "mt-4")
26
24
  "btn btn-primary mt-4"
27
25
  """
28
- result = []
29
-
26
+ result: list[str] = []
27
+
30
28
  for class_ in classes:
31
29
  if class_ is None:
32
30
  continue
@@ -35,38 +33,44 @@ def stringify(*classes: Optional[Union[str, List[str]]]) -> str:
35
33
  result.extend(c for c in class_ if c)
36
34
  elif isinstance(class_, str) and class_.strip():
37
35
  result.append(class_.strip())
38
-
36
+
39
37
  return " ".join(result)
40
38
 
41
39
 
42
- def get_eidos_static_directory() -> str:
40
+ def get_eidos_static_files(markdown: bool = False) -> dict:
43
41
  """
44
- Get the path to eidos static files for mounting in FastAPI/Air apps.
45
-
46
- This function returns the directory containing the eidos package files,
47
- which includes the CSS directory. Use this when mounting static files
48
- in your application.
49
-
42
+ Get a dictionary mapping URL paths to static file directories.
43
+
44
+ This provides a safe way to mount only specific static assets
45
+ without exposing Python source files.
46
+
47
+ Args:
48
+ markdown: Whether to include markdown plugin CSS (default: False)
49
+
50
50
  Returns:
51
- The absolute path to the eidos package directory
52
-
51
+ Dict mapping mount paths to directory paths
52
+
53
53
  Example:
54
54
  >>> from fastapi.staticfiles import StaticFiles
55
- >>> from eidos.utils import get_eidos_static_directory
56
- >>> app.mount("/eidos", StaticFiles(directory=get_eidos_static_directory()), name="eidos")
55
+ >>> from eidos.utils import get_eidos_static_files
56
+ >>> # Basic usage - just core CSS and JS
57
+ >>> for mount_path, directory in get_eidos_static_files().items():
58
+ ... app.mount(mount_path, StaticFiles(directory=directory), name=mount_path.strip('/'))
59
+ >>>
60
+ >>> # Include markdown CSS
61
+ >>> for mount_path, directory in get_eidos_static_files(markdown=True).items():
62
+ ... app.mount(mount_path, StaticFiles(directory=directory), name=mount_path.strip('/'))
57
63
  """
58
- try:
59
- from importlib.resources import files
60
- import pathlib
61
- # Convert MultiplexedPath to actual filesystem path
62
- eidos_path = files('eidos')
63
- if hasattr(eidos_path, '_paths'):
64
- # MultiplexedPath - get the first valid path
65
- for path in eidos_path._paths:
66
- if isinstance(path, pathlib.Path) and path.exists():
67
- return str(path)
68
- # Try to get the path directly
69
- return str(eidos_path)
70
- except (ImportError, AttributeError):
71
- # Fallback for development or if importlib.resources fails
72
- return os.path.dirname(os.path.abspath(__file__))
64
+ # Use pathlib for cleaner path handling
65
+ base_path = Path(__file__).parent.absolute()
66
+
67
+ static_files = {
68
+ "/eidos/css": str(base_path / "css"),
69
+ "/eidos/js": str(base_path / "js"),
70
+ }
71
+
72
+ # Only include markdown CSS if requested
73
+ if markdown:
74
+ static_files["/eidos/plugins/markdown/css"] = str(base_path / "plugins" / "markdown" / "css")
75
+
76
+ return static_files
@@ -0,0 +1,113 @@
1
+ Metadata-Version: 2.4
2
+ Name: eidosui
3
+ Version: 0.5.0
4
+ Summary: A modern, Tailwind CSS-based UI library for air development
5
+ Project-URL: Homepage, https://github.com/isaac-flath/EidosUI
6
+ Project-URL: Repository, https://github.com/isaac-flath/EidosUI
7
+ Project-URL: Issues, https://github.com/isaac-flath/EidosUI/issues
8
+ Project-URL: Documentation, https://github.com/isaac-flath/EidosUI#readme
9
+ Author: Isaac Flath
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: air,components,css,fastapi,tailwind,ui,web
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: air>=0.12
25
+ Requires-Dist: fastapi[standard]
26
+ Requires-Dist: uvicorn
27
+ Provides-Extra: dev
28
+ Requires-Dist: black; extra == 'dev'
29
+ Requires-Dist: isort; extra == 'dev'
30
+ Requires-Dist: mypy; extra == 'dev'
31
+ Requires-Dist: pytest; extra == 'dev'
32
+ Requires-Dist: ruff; extra == 'dev'
33
+ Provides-Extra: markdown
34
+ Requires-Dist: markdown>=3.4; extra == 'markdown'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # EidosUI
38
+
39
+ Modern UI library for Python web frameworks. Built on Air and Tailwind CSS.
40
+
41
+ > [!CAUTION]
42
+ > This library is in alpha, and may have semi-frequent breaking changes. I'd love for you to try it an contribute feedback or PRs!
43
+
44
+ ## Installation
45
+
46
+ ```bash
47
+ pip install eidosui
48
+ ```
49
+
50
+ ## Quick Start
51
+
52
+ ```python
53
+ from eidos import *
54
+ import air
55
+
56
+ app = air.Air()
57
+
58
+ @app.get("/")
59
+ def home():
60
+ return Html(
61
+ Head(
62
+ Title("My App"),
63
+ *EidosHeaders() # Required CSS/JS
64
+ ),
65
+ Body(
66
+ H1("Welcome"),
67
+ P("Build modern web apps with Python."),
68
+ DataTable.from_lists(
69
+ [["Alice", "30"], ["Bob", "25"]],
70
+ headers=["Name", "Age"]
71
+ )
72
+ )
73
+ )
74
+
75
+ app.run()
76
+ ```
77
+
78
+ ## Features
79
+
80
+ - **Styled HTML tags** - Pre-styled versions of all HTML elements
81
+ - **Components** - DataTable, NavBar, and more
82
+ - **Themes** - Light/dark themes via CSS variables
83
+ - **Type hints** - Full type annotations
84
+ - **Air integration** - Works seamlessly with Air framework
85
+
86
+ ## Plugins
87
+
88
+ ### Markdown
89
+
90
+ ```bash
91
+ pip install "eidosui[markdown]"
92
+ ```
93
+
94
+ ```python
95
+ from eidos.plugins.markdown import Markdown, MarkdownCSS
96
+
97
+ Head(
98
+ *EidosHeaders(),
99
+ MarkdownCSS() # Add markdown styles
100
+ )
101
+
102
+ Body(
103
+ Markdown("# Hello\n\nSupports **GitHub Flavored Markdown**")
104
+ )
105
+ ```
106
+
107
+ ## Documentation
108
+
109
+ Full documentation: https://eidosui.readthedocs.io
110
+
111
+ ## License
112
+
113
+ MIT