logseq-python
Advanced tools
+35
-0
@@ -8,2 +8,37 @@ # Changelog | ||
| ## [0.4.0] - 2025-11-01 | ||
| ### ✨ Features | ||
| - **DiagramBuilder for visual diagrams** - Full support for diagram code blocks in Logseq | ||
| - **Mermaid diagrams:** flowchart, sequence, gantt, class, state, ER, pie charts | ||
| - **Graphviz:** directed and undirected graphs with helper methods | ||
| - **PlantUML:** sequence diagrams, class diagrams, and more | ||
| - **Additional formats:** Ditaa, Vega, Vega-Lite, Excalidraw support | ||
| - Convenience method `page.diagram('mermaid')` for easy integration | ||
| - Diagrams work seamlessly in pages and as sub-blocks | ||
| - Helper methods for common diagram types (e.g., `mermaid_flowchart()`, `graphviz_digraph()`) | ||
| ### ✅ Testing | ||
| - **14 new diagram tests** - Complete coverage of all diagram types | ||
| - Tests for all Mermaid diagram types (7 types) | ||
| - Graphviz directed and undirected graphs | ||
| - PlantUML start/end tags | ||
| - Diagrams in blocks and pages | ||
| - Multiple diagrams in single page | ||
| - **Total: 68 passing builder tests** | ||
| ### 📚 Examples | ||
| ```python | ||
| # Mermaid flowchart | ||
| page.diagram('mermaid').mermaid_flowchart('LR').line('A --> B') | ||
| # Graphviz | ||
| diagram = DiagramBuilder('graphviz') | ||
| diagram.graphviz_digraph().line(' A -> B;').close_block() | ||
| # PlantUML | ||
| diagram = DiagramBuilder('plantuml') | ||
| diagram.plantuml_start().line('Alice -> Bob').plantuml_end() | ||
| ``` | ||
| ## [0.3.3] - 2025-11-01 | ||
@@ -10,0 +45,0 @@ |
@@ -51,2 +51,3 @@ """ | ||
| MediaBuilder, | ||
| DiagramBuilder, | ||
| DrawingBuilder | ||
@@ -90,2 +91,3 @@ ) | ||
| 'MediaBuilder', | ||
| 'DiagramBuilder', | ||
| 'DrawingBuilder', | ||
@@ -92,0 +94,0 @@ |
@@ -483,2 +483,152 @@ """ | ||
| class DiagramBuilder(ContentBuilder): | ||
| """Builder for diagram code blocks (Mermaid, Graphviz, PlantUML, etc.). | ||
| Logseq supports rendering diagrams directly from code blocks with specific | ||
| language identifiers like 'mermaid', 'graphviz', 'plantuml', etc. | ||
| Examples: | ||
| # Mermaid flowchart | ||
| diagram = DiagramBuilder('mermaid') | ||
| diagram.line('graph TD') | ||
| diagram.line(' A[Start] --> B{Decision}') | ||
| diagram.line(' B -->|Yes| C[Do Something]') | ||
| diagram.line(' B -->|No| D[Do Nothing]') | ||
| # Graphviz diagram | ||
| diagram = DiagramBuilder('graphviz') | ||
| diagram.line('digraph G {') | ||
| diagram.line(' A -> B;') | ||
| diagram.line(' B -> C;') | ||
| diagram.line('}') | ||
| """ | ||
| # Supported diagram types in Logseq | ||
| SUPPORTED_TYPES = { | ||
| 'mermaid', 'graphviz', 'dot', 'plantuml', 'ditaa', | ||
| 'vega', 'vega-lite', 'excalidraw' | ||
| } | ||
| def __init__(self, diagram_type: str = 'mermaid'): | ||
| super().__init__() | ||
| self._diagram_type = diagram_type.lower() | ||
| self._lines: List[str] = [] | ||
| def type(self, diagram_type: str) -> 'DiagramBuilder': | ||
| """Set the diagram type. | ||
| Args: | ||
| diagram_type: Type of diagram (mermaid, graphviz, plantuml, etc.) | ||
| """ | ||
| self._diagram_type = diagram_type.lower() | ||
| return self | ||
| def line(self, content: str) -> 'DiagramBuilder': | ||
| """Add a line to the diagram.""" | ||
| self._lines.append(content) | ||
| return self | ||
| def lines(self, *contents: str) -> 'DiagramBuilder': | ||
| """Add multiple lines to the diagram.""" | ||
| self._lines.extend(contents) | ||
| return self | ||
| def blank_line(self) -> 'DiagramBuilder': | ||
| """Add a blank line.""" | ||
| self._lines.append("") | ||
| return self | ||
| # Mermaid-specific helpers | ||
| def mermaid_flowchart(self, direction: str = 'TD') -> 'DiagramBuilder': | ||
| """Start a Mermaid flowchart. | ||
| Args: | ||
| direction: Flow direction - TD (top-down), LR (left-right), etc. | ||
| """ | ||
| self._diagram_type = 'mermaid' | ||
| self._lines.append(f'graph {direction}') | ||
| return self | ||
| def mermaid_sequence(self) -> 'DiagramBuilder': | ||
| """Start a Mermaid sequence diagram.""" | ||
| self._diagram_type = 'mermaid' | ||
| self._lines.append('sequenceDiagram') | ||
| return self | ||
| def mermaid_gantt(self) -> 'DiagramBuilder': | ||
| """Start a Mermaid Gantt chart.""" | ||
| self._diagram_type = 'mermaid' | ||
| self._lines.append('gantt') | ||
| return self | ||
| def mermaid_class_diagram(self) -> 'DiagramBuilder': | ||
| """Start a Mermaid class diagram.""" | ||
| self._diagram_type = 'mermaid' | ||
| self._lines.append('classDiagram') | ||
| return self | ||
| def mermaid_state_diagram(self) -> 'DiagramBuilder': | ||
| """Start a Mermaid state diagram.""" | ||
| self._diagram_type = 'mermaid' | ||
| self._lines.append('stateDiagram-v2') | ||
| return self | ||
| def mermaid_er_diagram(self) -> 'DiagramBuilder': | ||
| """Start a Mermaid entity-relationship diagram.""" | ||
| self._diagram_type = 'mermaid' | ||
| self._lines.append('erDiagram') | ||
| return self | ||
| def mermaid_pie(self) -> 'DiagramBuilder': | ||
| """Start a Mermaid pie chart.""" | ||
| self._diagram_type = 'mermaid' | ||
| self._lines.append('pie') | ||
| return self | ||
| # Graphviz-specific helpers | ||
| def graphviz_digraph(self, name: str = 'G') -> 'DiagramBuilder': | ||
| """Start a Graphviz directed graph. | ||
| Args: | ||
| name: Name of the graph | ||
| """ | ||
| self._diagram_type = 'graphviz' | ||
| self._lines.append(f'digraph {name} {{') | ||
| return self | ||
| def graphviz_graph(self, name: str = 'G') -> 'DiagramBuilder': | ||
| """Start a Graphviz undirected graph. | ||
| Args: | ||
| name: Name of the graph | ||
| """ | ||
| self._diagram_type = 'graphviz' | ||
| self._lines.append(f'graph {name} {{') | ||
| return self | ||
| def close_block(self) -> 'DiagramBuilder': | ||
| """Close a block (useful for Graphviz, PlantUML).""" | ||
| self._lines.append('}') | ||
| return self | ||
| # PlantUML-specific helpers | ||
| def plantuml_start(self) -> 'DiagramBuilder': | ||
| """Start a PlantUML diagram.""" | ||
| self._diagram_type = 'plantuml' | ||
| self._lines.append('@startuml') | ||
| return self | ||
| def plantuml_end(self) -> 'DiagramBuilder': | ||
| """End a PlantUML diagram.""" | ||
| self._lines.append('@enduml') | ||
| return self | ||
| def build(self) -> str: | ||
| """Build the diagram code block.""" | ||
| lines = [f"```{self._diagram_type}"] | ||
| lines.extend(self._lines) | ||
| lines.append("```") | ||
| return "\n".join(lines) | ||
| class DrawingBuilder(ContentBuilder): | ||
@@ -502,2 +652,2 @@ """Builder for drawing/whiteboard blocks.""" | ||
| def build(self) -> str: | ||
| return f"{{{{drawing {self._drawing_id}}}}}" | ||
| return f"{{{{drawing {self._drawing_id}}}}}" |
@@ -266,2 +266,20 @@ """ | ||
| def diagram(self, diagram_type: str = 'mermaid') -> 'DiagramBuilder': | ||
| """Create and add a diagram code block, returning it for chaining. | ||
| Args: | ||
| diagram_type: Type of diagram (mermaid, graphviz, plantuml, etc.) | ||
| Examples: | ||
| # Mermaid flowchart | ||
| page.diagram('mermaid').mermaid_flowchart().line('A --> B') | ||
| # Graphviz | ||
| page.diagram('graphviz').graphviz_digraph().line(' A -> B;').close_block() | ||
| """ | ||
| from .content_types import DiagramBuilder | ||
| diagram = DiagramBuilder(diagram_type) | ||
| self._content_blocks.append(diagram) | ||
| return diagram | ||
| def add(self, builder: Union[ContentBuilder, str]) -> 'PageBuilder': | ||
@@ -268,0 +286,0 @@ """Add a content builder or raw string.""" |
| Metadata-Version: 2.4 | ||
| Name: logseq-python | ||
| Version: 0.3.3 | ||
| Version: 0.4.0 | ||
| Summary: A comprehensive Python library for working with Logseq knowledge graphs | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/thinmanj/logseq-python-library |
+1
-1
| Metadata-Version: 2.4 | ||
| Name: logseq-python | ||
| Version: 0.3.3 | ||
| Version: 0.4.0 | ||
| Summary: A comprehensive Python library for working with Logseq knowledge graphs | ||
@@ -5,0 +5,0 @@ Home-page: https://github.com/thinmanj/logseq-python-library |
+1
-1
@@ -7,3 +7,3 @@ [build-system] | ||
| name = "logseq-python" | ||
| version = "0.3.3" | ||
| version = "0.4.0" | ||
| description = "A comprehensive Python library for working with Logseq knowledge graphs" | ||
@@ -10,0 +10,0 @@ readme = "README.md" |
+184
-1
@@ -18,3 +18,3 @@ """ | ||
| CodeBlockBuilder, TaskBuilder, TableBuilder, QuoteBuilder, | ||
| ListBuilder, TextBuilder, MathBuilder, MediaBuilder | ||
| ListBuilder, TextBuilder, MathBuilder, MediaBuilder, DiagramBuilder | ||
| ) | ||
@@ -648,2 +648,185 @@ from logseq_py.builders.advanced_builders import QueryBuilder, JournalBuilder, WorkflowBuilder | ||
| class TestDiagramBuilder: | ||
| """Test DiagramBuilder for Mermaid, Graphviz, PlantUML diagrams.""" | ||
| def test_mermaid_flowchart(self): | ||
| """Mermaid flowchart diagram.""" | ||
| diagram = DiagramBuilder('mermaid') | ||
| diagram.mermaid_flowchart('TD') | ||
| diagram.line(' A[Start] --> B[End]') | ||
| result = diagram.build() | ||
| assert '```mermaid' in result | ||
| assert 'graph TD' in result | ||
| assert 'A[Start] --> B[End]' in result | ||
| assert result.endswith('```') | ||
| def test_mermaid_sequence_diagram(self): | ||
| """Mermaid sequence diagram.""" | ||
| diagram = DiagramBuilder() | ||
| diagram.mermaid_sequence() | ||
| diagram.line(' Alice->>Bob: Hello') | ||
| diagram.line(' Bob-->>Alice: Hi') | ||
| result = diagram.build() | ||
| assert 'sequenceDiagram' in result | ||
| assert 'Alice->>Bob' in result | ||
| def test_mermaid_gantt(self): | ||
| """Mermaid Gantt chart.""" | ||
| diagram = DiagramBuilder('mermaid') | ||
| diagram.mermaid_gantt() | ||
| diagram.line(' title Project Schedule') | ||
| diagram.line(' section Phase 1') | ||
| diagram.line(' Task 1: 2025-01-01, 7d') | ||
| result = diagram.build() | ||
| assert 'gantt' in result | ||
| assert 'Project Schedule' in result | ||
| def test_mermaid_class_diagram(self): | ||
| """Mermaid class diagram.""" | ||
| diagram = DiagramBuilder('mermaid') | ||
| diagram.mermaid_class_diagram() | ||
| diagram.line(' class Animal {') | ||
| diagram.line(' +name: string') | ||
| diagram.line(' }') | ||
| result = diagram.build() | ||
| assert 'classDiagram' in result | ||
| assert 'class Animal' in result | ||
| def test_mermaid_state_diagram(self): | ||
| """Mermaid state diagram.""" | ||
| diagram = DiagramBuilder('mermaid') | ||
| diagram.mermaid_state_diagram() | ||
| diagram.line(' [*] --> Active') | ||
| diagram.line(' Active --> [*]') | ||
| result = diagram.build() | ||
| assert 'stateDiagram-v2' in result | ||
| def test_mermaid_er_diagram(self): | ||
| """Mermaid ER diagram.""" | ||
| diagram = DiagramBuilder('mermaid') | ||
| diagram.mermaid_er_diagram() | ||
| diagram.line(' CUSTOMER ||--o{ ORDER : places') | ||
| result = diagram.build() | ||
| assert 'erDiagram' in result | ||
| assert 'CUSTOMER' in result | ||
| def test_mermaid_pie_chart(self): | ||
| """Mermaid pie chart.""" | ||
| diagram = DiagramBuilder('mermaid') | ||
| diagram.mermaid_pie() | ||
| diagram.line(' "Apples" : 40') | ||
| diagram.line(' "Oranges" : 60') | ||
| result = diagram.build() | ||
| assert 'pie' in result | ||
| assert 'Apples' in result | ||
| def test_graphviz_digraph(self): | ||
| """Graphviz directed graph.""" | ||
| diagram = DiagramBuilder('graphviz') | ||
| diagram.graphviz_digraph('MyGraph') | ||
| diagram.line(' A -> B;') | ||
| diagram.line(' B -> C;') | ||
| diagram.close_block() | ||
| result = diagram.build() | ||
| assert '```graphviz' in result | ||
| assert 'digraph MyGraph {' in result | ||
| assert 'A -> B;' in result | ||
| assert result.count('}') >= 1 | ||
| def test_graphviz_graph(self): | ||
| """Graphviz undirected graph.""" | ||
| diagram = DiagramBuilder('graphviz') | ||
| diagram.graphviz_graph('UndirectedGraph') | ||
| diagram.line(' A -- B;') | ||
| diagram.close_block() | ||
| result = diagram.build() | ||
| assert 'graph UndirectedGraph {' in result | ||
| assert 'A -- B;' in result | ||
| def test_plantuml(self): | ||
| """PlantUML diagram.""" | ||
| diagram = DiagramBuilder('plantuml') | ||
| diagram.plantuml_start() | ||
| diagram.line('Alice -> Bob: Request') | ||
| diagram.line('Bob --> Alice: Response') | ||
| diagram.plantuml_end() | ||
| result = diagram.build() | ||
| assert '```plantuml' in result | ||
| assert '@startuml' in result | ||
| assert '@enduml' in result | ||
| assert 'Alice -> Bob' in result | ||
| def test_diagram_with_blank_lines(self): | ||
| """Diagram with blank lines for readability.""" | ||
| diagram = DiagramBuilder('mermaid') | ||
| diagram.mermaid_flowchart() | ||
| diagram.line(' A --> B') | ||
| diagram.blank_line() | ||
| diagram.line(' C --> D') | ||
| result = diagram.build() | ||
| lines = result.split('\n') | ||
| # Should have blank line between the two connections | ||
| assert '' in [line.strip() for line in lines[2:-1]] | ||
| def test_diagram_in_block(self): | ||
| """Diagram as child block.""" | ||
| parent = BlockBuilder('Architecture diagram:') | ||
| diagram = DiagramBuilder('mermaid') | ||
| diagram.mermaid_flowchart() | ||
| diagram.line(' Start --> End') | ||
| parent.child(BlockBuilder(diagram.build())) | ||
| result = parent.build() | ||
| lines = result.split('\n') | ||
| assert lines[0] == '- Architecture diagram:' | ||
| assert lines[1] == ' - ```mermaid' | ||
| assert 'graph TD' in lines[2] | ||
| def test_page_with_diagram(self): | ||
| """Page with diagram using convenience method.""" | ||
| page = PageBuilder('System Design') | ||
| page.heading(1, 'Architecture') | ||
| diagram = page.diagram('mermaid') | ||
| diagram.mermaid_flowchart('LR') | ||
| diagram.line(' Client --> Server') | ||
| result = page.build() | ||
| assert '# Architecture' in result | ||
| assert '```mermaid' in result | ||
| assert 'graph LR' in result | ||
| def test_multiple_diagrams_in_page(self): | ||
| """Page with multiple different diagram types.""" | ||
| page = PageBuilder('Documentation') | ||
| # Mermaid | ||
| page.heading(2, 'Flow') | ||
| flow = page.diagram('mermaid') | ||
| flow.mermaid_flowchart() | ||
| flow.line(' A --> B') | ||
| # Graphviz | ||
| page.heading(2, 'Relationships') | ||
| graph = page.diagram('graphviz') | ||
| graph.graphviz_digraph() | ||
| graph.line(' X -> Y;') | ||
| graph.close_block() | ||
| result = page.build() | ||
| assert '```mermaid' in result | ||
| assert '```graphviz' in result | ||
| class TestIntegrationScenarios: | ||
@@ -650,0 +833,0 @@ """Test realistic usage scenarios.""" |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
903800
1.46%17448
1.68%