eidosui 0.4.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,29 +1,136 @@
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
8
  # Define exports for this module
7
9
  __all__ = [
8
10
  # Custom EidosUI components
9
- 'Button', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'Body',
11
+ "Button",
12
+ "H1",
13
+ "H2",
14
+ "H3",
15
+ "H4",
16
+ "H5",
17
+ "H6",
18
+ "Body",
10
19
  # Semantic components with styling
11
- 'Strong', 'I', 'Small', 'Del', 'Abbr', 'Var', 'Mark', 'Time',
12
- 'Code', 'Pre', 'Kbd', 'Samp', 'Blockquote', 'Cite', 'Address',
13
- 'Hr', 'Details', 'Summary', 'Dl', 'Dt', 'Dd', 'Figure', 'Figcaption',
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",
14
51
  # Pass-through HTML tags from air.tags
15
- 'A', 'Area', 'Article', 'Aside', 'Audio', 'B', 'Base', 'Bdi', 'Bdo', 'Br',
16
- 'Canvas', 'Caption', 'Col', 'Colgroup', 'Data', 'Datalist', 'Dfn', 'Dialog', 'Div',
17
- 'Em', 'Embed', 'Fieldset', 'Footer', 'Form', 'Head', 'Header', 'Hgroup', 'Html',
18
- 'Iframe', 'Img', 'Input', 'Ins', 'Label', 'Legend', 'Li', 'Link',
19
- 'Main', 'Map', 'Menu', 'Meta', 'Meter', 'Nav', 'Noscript', 'Object', 'Ol', 'Optgroup', 'Option', 'Output',
20
- 'P', 'Param', 'Picture', 'Progress', 'Q', 'Rp', 'Rt', 'Ruby',
21
- 'S', 'Script', 'Search', 'Section', 'Select', 'Source', 'Span', 'Style', 'Sub', 'Sup',
22
- 'Table', 'Tbody', 'Td', 'Template', 'Textarea', 'Tfoot', 'Th', 'Thead', 'Title', 'Tr', 'Track',
23
- 'U', 'Ul', 'Video', 'Wbr'
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",
24
126
  ]
25
127
 
26
- def Button(*content: Any, class_: Optional[Union[str, list[str]]] = styles.buttons.primary, **kwargs: Any) -> air.Tag:
128
+
129
+ def Button(
130
+ *content: Any,
131
+ class_: str | list[str] | None = styles.buttons.primary,
132
+ **kwargs: Any,
133
+ ) -> air.Tag:
27
134
  """
28
135
  Args:
29
136
  content: The content of the button.
@@ -38,7 +145,8 @@ def Button(*content: Any, class_: Optional[Union[str, list[str]]] = styles.butto
38
145
  """
39
146
  return air.Button(*content, class_=stringify(styles.buttons.base, class_), **kwargs)
40
147
 
41
- 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:
42
150
  """
43
151
  Args:
44
152
  content: The content of the h1 tag.
@@ -53,7 +161,8 @@ def H1(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs:
53
161
  """
54
162
  return air.H1(*content, class_=stringify(styles.typography.h1, class_), **kwargs)
55
163
 
56
- 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:
57
166
  """
58
167
  Args:
59
168
  content: The content of the h2 tag.
@@ -68,7 +177,8 @@ def H2(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs:
68
177
  """
69
178
  return air.H2(*content, class_=stringify(styles.typography.h2, class_), **kwargs)
70
179
 
71
- 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:
72
182
  """
73
183
  Args:
74
184
  content: The content of the h3 tag.
@@ -83,33 +193,43 @@ def H3(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs:
83
193
  """
84
194
  return air.H3(*content, class_=stringify(styles.typography.h3, class_), **kwargs)
85
195
 
86
- 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:
87
198
  return air.H4(*content, class_=stringify(styles.typography.h4, class_), **kwargs)
88
199
 
89
- 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:
90
202
  return air.H5(*content, class_=stringify(styles.typography.h5, class_), **kwargs)
91
203
 
92
- 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:
93
206
  return air.H6(*content, class_=stringify(styles.typography.h6, class_), **kwargs)
94
207
 
95
- 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:
96
210
  return air.Body(*content, class_=stringify(styles.Theme.body, class_), **kwargs)
97
211
 
212
+
98
213
  # Semantic HTML Elements
99
214
 
100
- 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:
101
217
  return air.Strong(*content, class_=stringify(styles.semantic.strong, class_), **kwargs)
102
218
 
103
- 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:
104
221
  return air.I(*content, class_=stringify(styles.semantic.i, class_), **kwargs)
105
222
 
106
- 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:
107
225
  return air.Small(*content, class_=stringify(styles.semantic.small, class_), **kwargs)
108
226
 
109
- 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:
110
229
  return air.Del(*content, class_=stringify(styles.semantic.del_, class_), **kwargs)
111
230
 
112
- 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:
113
233
  """
114
234
  Args:
115
235
  content: The content of the abbr tag.
@@ -124,71 +244,192 @@ def Abbr(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs
124
244
  """
125
245
  return air.Abbr(*content, class_=stringify(styles.semantic.abbr, class_), **kwargs)
126
246
 
127
- 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:
128
249
  return air.Var(*content, class_=stringify(styles.semantic.var, class_), **kwargs)
129
250
 
130
- 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:
131
253
  return air.Mark(*content, class_=stringify(styles.semantic.mark, class_), **kwargs)
132
254
 
133
- 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:
134
257
  return air.Time(*content, class_=stringify(styles.semantic.time, class_), **kwargs)
135
258
 
136
- 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:
137
261
  return air.Code(*content, class_=stringify(styles.semantic.code, class_), **kwargs)
138
262
 
139
- 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:
140
265
  return air.Pre(*content, class_=stringify(styles.semantic.pre, class_), **kwargs)
141
266
 
142
- 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:
143
269
  return air.Kbd(*content, class_=stringify(styles.semantic.kbd, class_), **kwargs)
144
270
 
145
- 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:
146
273
  return air.Samp(*content, class_=stringify(styles.semantic.samp, class_), **kwargs)
147
274
 
148
- 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:
149
277
  return air.Blockquote(*content, class_=stringify(styles.semantic.blockquote, class_), **kwargs)
150
278
 
151
- 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:
152
281
  return air.Cite(*content, class_=stringify(styles.semantic.cite, class_), **kwargs)
153
282
 
154
- 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:
155
285
  return air.Address(*content, class_=stringify(styles.semantic.address, class_), **kwargs)
156
286
 
157
- 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:
158
289
  return air.Hr(class_=stringify(styles.semantic.hr, class_), **kwargs)
159
290
 
160
- 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:
161
293
  return air.Details(*content, class_=stringify(styles.semantic.details, class_), **kwargs)
162
294
 
163
- 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:
164
297
  return air.Summary(*content, class_=stringify(styles.semantic.summary, class_), **kwargs)
165
298
 
166
- 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:
167
301
  return air.Dl(*content, class_=stringify(styles.semantic.dl, class_), **kwargs)
168
302
 
169
- 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:
170
305
  return air.Dt(*content, class_=stringify(styles.semantic.dt, class_), **kwargs)
171
306
 
172
- 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:
173
309
  return air.Dd(*content, class_=stringify(styles.semantic.dd, class_), **kwargs)
174
310
 
175
- 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:
176
313
  return air.Figure(*content, class_=stringify(styles.semantic.figure, class_), **kwargs)
177
314
 
178
- def Figcaption(*content: Any, class_: Optional[Union[str, list[str]]] = None, **kwargs: Any) -> air.Tag:
315
+
316
+ def Figcaption(*content: Any, class_: str | list[str] | None = None, **kwargs: Any) -> air.Tag:
179
317
  return air.Figcaption(*content, class_=stringify(styles.semantic.figcaption, class_), **kwargs)
180
318
 
181
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
+
182
358
  # Pass-through tags from air.tags
183
359
  # Import all standard HTML tags that don't have custom styling
184
360
  from air.tags import (
185
- A, Area, Article, Aside, Audio, B, Base, Bdi, Bdo, Br,
186
- Canvas, Caption, Col, Colgroup, Data, Datalist, Dfn, Dialog, Div,
187
- Em, Embed, Fieldset, Footer, Form, Head, Header, Hgroup, Html,
188
- Iframe, Img, Input, Ins, Label, Legend, Li, Link,
189
- Main, Map, Menu, Meta, Meter, Nav, Noscript, Object, Ol, Optgroup, Option, Output,
190
- P, Param, Picture, Progress, Q, Rp, Rt, Ruby,
191
- S, Script, Search, Section, Select, Source, Span, Style, Sub, Sup,
192
- Table, Tbody, Td, Template, Textarea, Tfoot, Th, Thead, Title, Tr, Track,
193
- U, Ul, Video, Wbr
194
- )
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