cnotebook 2.1.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.
cnotebook/render.py ADDED
@@ -0,0 +1,200 @@
1
+ import logging
2
+ import base64
3
+ from .context import CNotebookContext, pass_cnotebook_context
4
+ from openeye import oechem, oedepict
5
+
6
+ log = logging.getLogger("cnotebook")
7
+
8
+
9
+ ########################################################################################################################
10
+ # Renderers for specific types
11
+ ########################################################################################################################
12
+
13
+ def create_img_tag(
14
+ width: float,
15
+ height: float,
16
+ image_mime_type: str,
17
+ image_bytes: bytes,
18
+ wrap_svg: bool = True
19
+
20
+ ) -> str:
21
+ """
22
+ Create the <img> HTML tag for rendering image bytes. This could be either plain text (in the case of SVG) or base64
23
+ encoded (in binary format cases).
24
+ :param width: Image width
25
+ :param height: Image height
26
+ :param image_mime_type: Image MIME type
27
+ :param image_bytes: Image bytes
28
+ :param wrap_svg: Wrap SVG in a specifically sized <div> tag for maximum control of size
29
+ :return: Image tag
30
+ """
31
+ if image_mime_type == "image/svg+xml":
32
+ if wrap_svg:
33
+ return '<div style=\'width:{}px;max-width:{}px;height:{}px;max-height:{}px\'>\n\t{}\n</div>'.format(
34
+ int(width),
35
+ int(width),
36
+ int(height),
37
+ int(height),
38
+ image_bytes.decode("utf-8")
39
+ )
40
+ else:
41
+ return image_bytes.decode("utf-8")
42
+
43
+ return '<img src=\'data:{};base64,{}\' style=\'width:{}px;max-width:{}px;height:{}px;max-height:{}px\' />'.format(
44
+ image_mime_type,
45
+ base64.b64encode(image_bytes).decode("utf-8"),
46
+ int(width),
47
+ int(width),
48
+ int(height),
49
+ int(height)
50
+ )
51
+
52
+
53
+ @pass_cnotebook_context
54
+ def oedisp_to_html(
55
+ disp: oedepict.OE2DMolDisplay,
56
+ *,
57
+ ctx: CNotebookContext
58
+ ) -> str:
59
+ """
60
+ Convert an OpenEye 2D molecule display object to HTML
61
+ :param ctx: Current molecule render context
62
+ :param disp: OpenEye 2D molecule display object
63
+ :return: HTML image tag
64
+ """
65
+ # Convert the display object to an <img> tag
66
+ image = oedepict.OEImage(disp.GetWidth(), disp.GetHeight())
67
+ oedepict.OERenderMolecule(image, disp)
68
+ image_bytes = oedepict.OEWriteImageToString(ctx.image_format, image)
69
+
70
+ return create_img_tag(
71
+ disp.GetWidth(),
72
+ disp.GetHeight(),
73
+ image_mime_type=ctx.image_mime_type,
74
+ image_bytes=image_bytes,
75
+ wrap_svg=ctx.structure_scale != oedepict.OEScale_AutoScale
76
+ )
77
+
78
+
79
+ def render_empty_molecule(*, ctx: CNotebookContext) -> str:
80
+ """
81
+ Render an image that says Empty Molecule
82
+ :param ctx: Render context
83
+ :return: Image tag
84
+ """
85
+ image = oedepict.OEImage(ctx.min_width, ctx.min_height)
86
+ image.DrawText(
87
+ oedepict.OE2DPoint(ctx.min_width / 2, ctx.min_height / 2),
88
+ "Empty Molecule",
89
+ oedepict.OEFont(
90
+ oedepict.OEFontFamily_Arial,
91
+ oedepict.OEFontStyle_Normal,
92
+ 14,
93
+ oedepict.OEAlignment_Center,
94
+ oechem.OEDarkBlue
95
+ )
96
+ )
97
+
98
+ return create_img_tag(
99
+ ctx.min_width,
100
+ ctx.min_height,
101
+ image_mime_type=ctx.image_mime_type,
102
+ image_bytes=oedepict.OEWriteImageToString(ctx.image_format, image),
103
+ wrap_svg=ctx.structure_scale != oedepict.OEScale_AutoScale
104
+ )
105
+
106
+
107
+ def render_invalid_molecule(*, ctx: CNotebookContext) -> str:
108
+ """
109
+ Render an image that says Empty Molecule
110
+ :param ctx: Render context
111
+ :return: Image tag
112
+ """
113
+ image = oedepict.OEImage(ctx.min_width, ctx.min_height)
114
+ image.DrawText(
115
+ oedepict.OE2DPoint(ctx.min_width / 2, ctx.min_height / 2),
116
+ "Invalid Molecule",
117
+ oedepict.OEFont(
118
+ oedepict.OEFontFamily_Arial,
119
+ oedepict.OEFontStyle_Normal,
120
+ 14,
121
+ oedepict.OEAlignment_Center,
122
+ oechem.OERed
123
+ )
124
+ )
125
+
126
+ return create_img_tag(
127
+ ctx.min_width,
128
+ ctx.min_height,
129
+ image_mime_type=ctx.image_mime_type,
130
+ image_bytes=oedepict.OEWriteImageToString(ctx.image_format, image),
131
+ wrap_svg=ctx.structure_scale != oedepict.OEScale_AutoScale
132
+ )
133
+
134
+
135
+ def oemol_to_disp(
136
+ mol: oechem.OEMolBase,
137
+ *,
138
+ ctx: CNotebookContext
139
+ ) -> oedepict.OE2DMolDisplay:
140
+ """
141
+ Convert a valid OpenEye molecule object to a display object for depiction. Note that it is highly recommended to
142
+ test that the molecule is valid first before calling this function.
143
+ :param ctx: Render context
144
+ :param mol: Molecule to convert
145
+ :return: Display object for depiction
146
+ """
147
+ # Only recalculate coordinates if we don't have a 2D structure
148
+ if mol.GetDimension() == 2:
149
+ oedepict.OEPrepareDepiction(mol, False)
150
+ else:
151
+ oedepict.OEPrepareDepiction(mol, True)
152
+
153
+ return ctx.create_molecule_display(mol)
154
+
155
+
156
+ @pass_cnotebook_context
157
+ def oemol_to_html(mol: oechem.OEMolBase, *, ctx: CNotebookContext) -> str:
158
+ """
159
+ Convert an OpenEye Molecule object to HTML
160
+ :param ctx: Render context
161
+ :param mol: Molecule to convert
162
+ :return: HTML string
163
+ """
164
+ # Render valid molecules
165
+ if mol.IsValid():
166
+
167
+ # Create the display object from the context
168
+ disp = oemol_to_disp(mol, ctx=ctx)
169
+
170
+ # Render the display
171
+ return oedisp_to_html(disp, ctx=ctx)
172
+
173
+ # Render empty molecules
174
+ elif mol.NumAtoms() == 0:
175
+ return render_empty_molecule(ctx=ctx)
176
+
177
+ # Render other invalid molecules
178
+ else:
179
+ return render_invalid_molecule(ctx=ctx)
180
+
181
+
182
+ @pass_cnotebook_context
183
+ def oeimage_to_html(image: oedepict.OEImage, *, ctx: CNotebookContext) -> str:
184
+ """
185
+ Convert an OEImage to HTML
186
+ :param ctx: Render context
187
+ :param image: Image to render
188
+ :return: HTML string
189
+ """
190
+ # Convert the image to an <img> tag
191
+ image_bytes = oedepict.OEWriteImageToString(ctx.image_format, image)
192
+ return create_img_tag(
193
+ image.GetWidth(),
194
+ image.GetHeight(),
195
+ image_mime_type=ctx.image_mime_type,
196
+ image_bytes=image_bytes,
197
+ wrap_svg=ctx.structure_scale != oedepict.OEScale_AutoScale
198
+ )
199
+
200
+
@@ -0,0 +1,336 @@
1
+ Metadata-Version: 2.4
2
+ Name: cnotebook
3
+ Version: 2.1.0
4
+ Summary: Chemistry visualization in Jupyter Notebooks with the OpenEye Toolkits
5
+ Author-email: Scott Arne Johnson <scott.arne.johnson@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/scott-arne/cnotebook
8
+ Project-URL: Bug Reports, https://github.com/scott-arne/cnotebook/issues
9
+ Project-URL: Source, https://github.com/scott-arne/cnotebook
10
+ Project-URL: Documentation, https://github.com/scott-arne/cnotebook#readme
11
+ Project-URL: Changelog, https://github.com/scott-arne/cnotebook/blob/master/CHANGELOG.md
12
+ Keywords: chemistry,cheminformatics,computational-chemistry,molecular-visualization,jupyter,marimo,openeye,scientific-computing
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
17
+ Classifier: Topic :: Scientific/Engineering :: Visualization
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3 :: Only
23
+ Classifier: Operating System :: OS Independent
24
+ Classifier: Framework :: Jupyter
25
+ Classifier: Framework :: IPython
26
+ Requires-Python: >=3.11
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: openeye-toolkits>=2025.2.1
30
+ Requires-Dist: anywidget>=0.9.0
31
+ Requires-Dist: jinja2>=3.0.0
32
+ Provides-Extra: dev
33
+ Requires-Dist: invoke; extra == "dev"
34
+ Requires-Dist: build; extra == "dev"
35
+ Requires-Dist: pytest; extra == "dev"
36
+ Provides-Extra: test
37
+ Requires-Dist: pytest; extra == "test"
38
+ Requires-Dist: pytest-cov; extra == "test"
39
+ Dynamic: license-file
40
+
41
+ # CNotebook
42
+
43
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
44
+ [![OpenEye Toolkits](https://img.shields.io/badge/OpenEye-2025.2.1+-green.svg)](https://www.eyesopen.com/toolkits)
45
+
46
+ **Author:** Scott Arne Johnson ([scott.arne.johnson@gmail.com](mailto:scott.arne.johnson@gmail.com))
47
+
48
+ CNotebook provides chemistry visualization for Jupyter Notebooks and Marimo using the OpenEye Toolkits.
49
+ Import the package and your molecular data will automatically render as chemical structures without additional
50
+ configuration.
51
+
52
+ Supports both Pandas and Polars DataFrames with automatic environment detection.
53
+
54
+ **Render molecules in Jupyter and Marimo with style**
55
+ <br>
56
+ <img src="docs/_static/molecule_with_style.png" height="200">
57
+
58
+ **Maintain Jupyter table formatting for Pandas and Polars**
59
+ <br>
60
+ <img src="docs/_static/simple_pandas.png" height="600">
61
+
62
+ **Compatible with native Marimo tables**
63
+ <br>
64
+ <img src="docs/_static/marimo_pandas_polars.png" height="600">
65
+
66
+ **Interactive molecule grids that support data**
67
+ <br>
68
+ <img src="docs/_static/simple_molgrid.png" height="300">
69
+
70
+ ## Table of Contents
71
+
72
+ - [Installation](#installation)
73
+ - [Getting Started](#getting-started)
74
+ - [Features](#features)
75
+ - [MolGrid Interactive Visualization](#molgrid-interactive-visualization)
76
+ - [DataFrame Integration](#dataframe-integration)
77
+ - [Advanced Usage](#advanced-usage)
78
+ - [Example Notebooks](#example-notebooks)
79
+ - [Documentation](#documentation)
80
+ - [Contributing](#contributing)
81
+ - [License](#license)
82
+
83
+ ## Installation
84
+
85
+ ```bash
86
+ pip install cnotebook
87
+ ```
88
+
89
+ **Prerequisites:**
90
+ - [OpenEye Toolkits](http://eyesopen.com): `pip install openeye-toolkits`
91
+ - You must have a valid license (free for academia).
92
+
93
+ **Optional backends:**
94
+ - Pandas support: `pip install pandas oepandas`
95
+ - Polars support: `pip install polars oepolars`
96
+
97
+ Both backends can be installed together, neither are required unless you want to work with DataFrames.
98
+
99
+ ## Getting Started
100
+
101
+ The fastest way to learn CNotebook is through the example notebooks in the `examples/` directory:
102
+
103
+ | Environment | Pandas | Polars | MolGrid |
104
+ |-------------|-----------------------------------------------------------------|-----------------------------------------------------------------|-------------------------------------------------------------------|
105
+ | **Jupyter** | [pandas_jupyter_demo.ipynb](examples/pandas_jupyter_demo.ipynb) | [polars_jupyter_demo.ipynb](examples/polars_jupyter_demo.ipynb) | [molgrid_jupyter_demo.ipynb](examples/molgrid_jupyter_demo.ipynb) |
106
+ | **Marimo** | [pandas_marimo_demo.py](examples/pandas_marimo_demo.py) | [polars_marimo_demo.py](examples/polars_marimo_demo.py) | [molgrid_marimo_demo.py](examples/molgrid_marimo_demo.py) |
107
+
108
+ ### Basic Usage
109
+
110
+ ```python
111
+ import cnotebook
112
+ from openeye import oechem
113
+
114
+ # Create a molecule (supports titles in SMILES)
115
+ mol = oechem.OEGraphMol()
116
+ oechem.OESmilesToMol(mol, "c1cnccc1 Benzene")
117
+
118
+ # Display it - automatically renders as a chemical structure
119
+ mol
120
+ ```
121
+ <img src="docs/_static/benzene.png" />
122
+
123
+ CNotebook registers formatters so OpenEye molecule objects display as chemical structures instead of text representations.
124
+
125
+ ## Features
126
+
127
+ ### Automatic Rendering
128
+ - Zero configuration required
129
+ - Supports Jupyter Notebooks and Marimo
130
+ - Automatic environment and backend detection
131
+
132
+ ### Molecule Support
133
+ - Direct rendering of `oechem.OEMolBase` objects
134
+ - Advanced rendering with `OE2DMolDisplay` options
135
+ - Pandas integration via OEPandas
136
+ - Polars integration via OEPolars
137
+
138
+ ### Visualization Options
139
+ - PNG (default) or SVG output
140
+ - Configurable width, height, and scaling
141
+ - Substructure highlighting with SMARTS patterns
142
+ - Molecular alignment to reference structures
143
+
144
+ ### MolGrid Interactive Visualization
145
+ - Paginated grid display for browsing molecules
146
+ - Text search across molecular properties
147
+ - SMARTS substructure filtering
148
+ - Selection tools with export to SMILES or CSV
149
+ - Information tooltips with molecular data
150
+ - DataFrame integration with automatic field detection
151
+
152
+ ### DataFrame Integration
153
+ - Automatic molecule column detection and rendering
154
+ - Per-row substructure highlighting
155
+ - Molecular alignment within DataFrames
156
+ - Fingerprint similarity visualization
157
+ - Property calculations on molecule columns
158
+
159
+ ## MolGrid Interactive Visualization
160
+
161
+ MolGrid provides an interactive grid for browsing molecular datasets with search and selection capabilities.
162
+
163
+ ### Basic Example
164
+
165
+ ```python
166
+ from cnotebook import MolGrid
167
+ from openeye import oechem
168
+
169
+ # Create molecules
170
+ molecules = []
171
+ for smi in ["CCO", "c1ccccc1", "CC(=O)O"]:
172
+ mol = oechem.OEGraphMol()
173
+ oechem.OESmilesToMol(mol, smi)
174
+ molecules.append(mol)
175
+
176
+ # Display interactive grid
177
+ grid = MolGrid(molecules)
178
+ grid.display()
179
+ ```
180
+ <img src="docs/_static/simple_molgrid.png" height="300">
181
+
182
+ ### Search and Filter
183
+
184
+ MolGrid provides two search modes:
185
+ - **Properties mode:** Search by molecular titles and configurable text fields
186
+ - **SMARTS mode:** Filter by substructure patterns with match highlighting
187
+
188
+ ### Selection
189
+
190
+ - Click molecules or checkboxes to select
191
+ - Use the menu for Select All, Clear, and Invert operations
192
+ - Export selections to SMILES or CSV files
193
+
194
+ ### Information Tooltips
195
+
196
+ - Hover over the information button to view molecular data
197
+ - Click to pin tooltips for comparing multiple molecules
198
+ - Configure displayed fields with the `data` parameter
199
+
200
+ ### DataFrame Integration
201
+
202
+ ```python
203
+ import pandas as pd
204
+ from cnotebook import MolGrid
205
+ from openeye import oechem, oemolprop
206
+
207
+ # Create DataFrame
208
+ df = pd.DataFrame(
209
+ {"Molecule": ["CCO", "c1ccccc1", "CC(=O)O"]}
210
+ ).chem.as_molecule("Molecule")
211
+
212
+ # Calculate some properties
213
+ df["MW"] = df.Molecule.apply(oechem.OECalculateMolecularWeight)
214
+ df["PSA"] = df.Molecule.apply(oemolprop.OEGet2dPSA)
215
+ df["HBA"] = df.Molecule.apply(oemolprop.OEGetHBondAcceptorCount)
216
+ df["HBD"] = df.Molecule.apply(oemolprop.OEGetHBondDonorCount)
217
+
218
+ # Display the grid (using the 'Molecule' column for structures)
219
+ grid = df.chem.molgrid("Molecule")
220
+ grid.display()
221
+ ```
222
+
223
+ This will display the same grid as above, but with molecule data if you click the "i".
224
+
225
+ ### Retrieving Selections
226
+
227
+ ```python
228
+ # Get selected molecules
229
+ selected_mols = grid.get_selection()
230
+
231
+ # Get selected indices
232
+ indices = grid.get_selection_indices()
233
+ ```
234
+
235
+ ## DataFrame Integration
236
+
237
+ ### Pandas DataFrames
238
+
239
+ ```python
240
+ import cnotebook
241
+ import oepandas as oepd
242
+
243
+ # Read the example unaligned molecules
244
+ df = oepd.read_sdf("examples/assets/rotations.sdf", no_title=True)
245
+
246
+ # Rename the "Molecule" column to "Original" so that we can
247
+ # see the original unaligned molecules
248
+ df = df.rename(columns={"Molecule": "Original"})
249
+
250
+ # Create a new molecule column called "Aligned" so that we can
251
+ # see the aligned molecules
252
+ df["Aligned"] = df.Original.chem.copy_molecules()
253
+
254
+ # Add substructure highlighting
255
+ df["Original"].chem.highlight("c1ccccc1")
256
+ df["Aligned"].chem.highlight("c1ccccc1")
257
+
258
+ # Align molecules to a reference
259
+ df["Aligned"].chem.align_depictions("first")
260
+
261
+ # Display the DataFrame
262
+ df
263
+ ```
264
+
265
+ <img src="docs/_static/pandas_highlight_and_align_dataframe.png" height="400">
266
+
267
+ ### Polars DataFrames
268
+
269
+ Same example as above using Polars instead of Pandas. The main difference is that some methods are called
270
+ from the DataFrame instead of the Series.
271
+
272
+ ```python
273
+ import cnotebook
274
+ import oepolars as oepl
275
+
276
+ # Read the example unaligned molecules
277
+ df = oepl.read_sdf("examples/assets/rotations.sdf", no_title=True)
278
+
279
+ # Rename the "Molecule" column to "Original" so that we can
280
+ # see the original unaligned molecules
281
+ df = df.rename({"Molecule": "Original"})
282
+
283
+ # # Create a new molecule column called "Aligned" so that we can
284
+ # # see the aligned molecules
285
+ df = df.chem.copy_molecules("Original", "Aligned")
286
+
287
+ # # Add substructure highlighting
288
+ df.chem.highlight("Original", "c1ccccc1")
289
+ df.chem.highlight("Aligned", "c1ccccc1")
290
+
291
+ # # Align molecules to a reference
292
+ df["Aligned"].chem.align_depictions("first")
293
+
294
+ # Display the DataFrame
295
+ df
296
+ ```
297
+
298
+ This will display the exact same table as above.
299
+
300
+ ## Example Notebooks
301
+
302
+ The `examples/` directory contains comprehensive tutorials for learning CNotebook:
303
+
304
+ ### Jupyter Notebooks
305
+
306
+ - **[pandas_jupyter_demo.ipynb](examples/pandas_jupyter_demo.ipynb)** - Complete Pandas integration tutorial covering molecule rendering, highlighting, alignment, and fingerprint similarity
307
+ - **[polars_jupyter_demo.ipynb](examples/polars_jupyter_demo.ipynb)** - Complete Polars integration tutorial with the same features adapted for Polars patterns
308
+ - **[molgrid_jupyter_demo.ipynb](examples/molgrid_jupyter_demo.ipynb)** - Interactive molecule grid tutorial with search, selection, and export features
309
+ - **[pandas_jupyter_svgs.ipynb](examples/pandas_jupyter_svgs.ipynb)** - SVG vs PNG rendering comparison and quality considerations
310
+
311
+ ### Marimo Applications
312
+
313
+ - **[pandas_marimo_demo.py](examples/pandas_marimo_demo.py)** - Pandas tutorial in reactive Marimo environment
314
+ - **[polars_marimo_demo.py](examples/polars_marimo_demo.py)** - Polars tutorial in reactive Marimo environment
315
+ - **[molgrid_marimo_demo.py](examples/molgrid_marimo_demo.py)** - MolGrid tutorial with reactive selection feedback
316
+
317
+ **Recommended starting point:** Begin with the MolGrid demo for your preferred environment, then explore the Pandas or Polars tutorials for DataFrame integration.
318
+
319
+ ## Contributing
320
+
321
+ Contributions are welcome. Please ensure your code:
322
+ - Follows existing code style and conventions
323
+ - Includes appropriate tests
324
+ - Works with both Jupyter and Marimo environments
325
+ - Maintains compatibility with OpenEye Toolkits
326
+ - Works with both Pandas and Polars when applicable
327
+
328
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines.
329
+
330
+ ## License
331
+
332
+ This project is licensed under the MIT License. See [LICENSE](LICENSE) for details.
333
+
334
+ ## Support
335
+
336
+ For bug reports, feature requests, or questions, please open an issue on GitHub or contact [scott.arne.johnson@gmail.com](mailto:scott.arne.johnson@gmail.com).
@@ -0,0 +1,16 @@
1
+ cnotebook/__init__.py,sha256=C5wYJ0-6bmccYcbKCekAat08vWyk_IyqSHvob6AWfMU,13746
2
+ cnotebook/align.py,sha256=wDJy79PvRG0O_XHCAXkGv2uqp6nL65g-NO-oQSnOkpI,17926
3
+ cnotebook/context.py,sha256=HKTx0Bxz0LfleaWjBco_AoSGR2iPrVY-0OsJ_ro6O0o,19229
4
+ cnotebook/helpers.py,sha256=TCcNfykhSi77FrTQC2_2W8G9lxP0n3S7V8tEiGcE0hg,7121
5
+ cnotebook/ipython_ext.py,sha256=Z6IhHg_YP6-becgruEDiE-tVkSB0SZTUU6R2MItaVa4,1874
6
+ cnotebook/marimo_ext.py,sha256=F2WBiM0F3vLcL9vAcZesB7XgIWULd7QiWe78dQy1h3s,9790
7
+ cnotebook/pandas_ext.py,sha256=iOURve2nDg1z37Jq56GjP1KyvgG7BH9DYu4hlTsfIzg,43323
8
+ cnotebook/polars_ext.py,sha256=AMWVGPdS8qbFH-lNcGrekPwT9vFUxZxq41Rlqx4jjB4,47725
9
+ cnotebook/render.py,sha256=jn47U5I0t0H5R_iydhBsz_vVdtBVXyQYwrMb_XG8zy0,6143
10
+ cnotebook/grid/__init__.py,sha256=yNcddI5PP-6BBXCNhRALvNrlgAn7UeR9ntrhmMMShu8,1804
11
+ cnotebook/grid/grid.py,sha256=y-uV_cNmazI3aDWbRI2JSuOB83jhdZbiivlsPS4wBSk,51217
12
+ cnotebook-2.1.0.dist-info/licenses/LICENSE,sha256=HbIgeZz-pWGC7BEnYFCQ-jfD1m_BfiosF9qjgWw64GU,1080
13
+ cnotebook-2.1.0.dist-info/METADATA,sha256=adbX-E5iLz0yJwJaK_X7FUyT-FNzrYyH-e4GPIvQP5M,11860
14
+ cnotebook-2.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
15
+ cnotebook-2.1.0.dist-info/top_level.txt,sha256=jzkieTjQwdNKfMwnoElvDDtNPkeLMjbvWbsbkSsboo8,10
16
+ cnotebook-2.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2025 Scott Arne Johnson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ cnotebook