Skip to content

Conversation

@mdroidian
Copy link
Contributor

@mdroidian mdroidian commented Jan 24, 2025

Summary by CodeRabbit

Release Notes: Discourse Graphs Obsidian Plugin v0.1.0

  • New Features

    • Introduced Discourse Graphs Obsidian plugin
    • Added settings management for plugin configuration
    • Implemented sample modal and command functionality
  • Documentation

    • Updated project README with detailed plugin information
    • Added comprehensive setup instructions for Obsidian plugin
  • Development

    • Configured build and development scripts
    • Set up TypeScript and React integration for Obsidian plugin
  • Environment

    • Added .env.example with plugin path configuration
    • Introduced new environment variable support

@vercel
Copy link

vercel bot commented Jan 24, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
discourse-graph ⬜️ Skipped (Inspect) Jan 26, 2025 6:31pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 24, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive setup for an Obsidian plugin called Discourse Graph. The changes span multiple files across the project, establishing a new Obsidian plugin with React integration, build scripts, development configurations, and documentation. The implementation includes TypeScript configurations, environment setup, React components for settings and modals, and utility functions for registering plugin commands.

Changes

File Change Summary
README.md Added Turborepo section, expanded "Apps & Packages" with clearer structure and application instructions
apps/obsidian/.env.example Added OBSIDIAN_PLUGIN_PATH environment variable
apps/obsidian/README.md Introduced "Discourse Graphs" section explaining plugin functionality
apps/obsidian/manifest.json Created new plugin manifest with metadata
apps/obsidian/package.json Added project configuration for Obsidian plugin
apps/obsidian/scripts/* Added build, compile, and dev scripts for plugin development
apps/obsidian/src/components/* Implemented React components for app context, modal, settings
apps/obsidian/src/index.ts Created main plugin class with settings and lifecycle methods
apps/obsidian/src/styles/styles.css Added modal content styling
apps/obsidian/src/utils/registerCommands.ts Implemented plugin command registration
apps/obsidian/tsconfig.json Added TypeScript configuration for Obsidian plugin
turbo.json Updated to pass OBSIDIAN_PLUGIN_PATH environment variable

Sequence Diagram

sequenceDiagram
    participant User
    participant ObsidianApp
    participant DiscourseGraphPlugin
    participant SettingsTab
    participant Commands

    User->>ObsidianApp: Install Plugin
    ObsidianApp->>DiscourseGraphPlugin: onload()
    DiscourseGraphPlugin->>SettingsTab: Create Settings
    DiscourseGraphPlugin->>Commands: Register Commands
    User->>Commands: Trigger Command
    Commands->>DiscourseGraphPlugin: Execute Action
    DiscourseGraphPlugin->>ObsidianApp: Perform Task
Loading

Possibly related PRs

Poem

🐰 A Rabbit's Ode to Discourse Graphs 🌿

In Obsidian's realm, a plugin takes flight,
Connecting knowledge with semantic might
React components dance, commands unfurl
Our graph of wisdom begins to swirl
Code hops, knowledge leaps - what delight! 🚀


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@@ -0,0 +1,29 @@
{
"name": "@discourse-graphs/obsidian",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't just use obsidian because the npm obsidian package is in our dependencies sad face. will probably namespace all of the plugins this way

@@ -0,0 +1,27 @@
import { compile } from "./compile";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will generalize all of the scripts/ to work for all apps (as best we can) later

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

},
},
{
name: "combineStyles",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -0,0 +1,3 @@
.modal-content {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just an example

@@ -0,0 +1,18 @@
{
"extends": "@repo/typescript-config/react-library.json",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs to be migrated as much as possible to the @repo/typescript-config
https://roamresearch.com/#/app/discourse-graphs/page/s6veJ_Zvr

@mdroidian
Copy link
Contributor Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 26, 2025

✅ Actions performed

Full review triggered.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Nitpick comments (20)
apps/obsidian/tsconfig.json (1)

17-17: Consider expanding TypeScript file patterns.

The current include pattern ["**/*.ts"] might be too restrictive. Since this is a React project, you might want to include .tsx files as well.

-  "include": ["**/*.ts"]
+  "include": ["**/*.ts", "**/*.tsx"]
apps/obsidian/scripts/compile.ts (2)

47-64: Handle non-existing or locked directories gracefully.
The readDir function logs an error if the directory doesn't exist but continues execution. If there are permission or lock issues, the function returns an empty array. Consider handling such errors more formally or rethrowing them to indicate a failure status, especially in production builds.


70-70: Consider consolidating environment variable checks.
You’re loading environment variables globally using dotenv.config() (line 8) and also referencing environment variables in args. It might be beneficial to unify your environment checks and defaults in one place to reduce potential confusion or re-initialization issues.

apps/obsidian/src/components/AppContext.tsx (1)

10-18: Enhance clarity with a dedicated props interface.
Creating a dedicated interface, e.g., interface ContextProviderProps { app: App; children: React.ReactNode; }, may improve type consistency and readability.

apps/obsidian/scripts/build.ts (2)

3-8: Validate or log the NODE_ENV setting.
You explicitly set NODE_ENV = "production" when it’s not defined, but you do not log or validate that the final value aligns with your expectations. Consider adding a brief log or check to confirm environment correctness and possibly warn if NODE_ENV was already set differently.


9-16: Surface esbuild warnings and partial failures.
This block indicates a console message for compile failures and terminates the process upon error, but warnings from esbuild may go unnoticed. Consider capturing and logging warnings as well to ensure no relevant issues slip by in production.

apps/obsidian/scripts/dev.ts (1)

19-26: Enhance error handling in main function

The error handling could be more informative by including error details in the console output.

 const main = async () => {
   try {
     await dev();
   } catch (error) {
-    console.error(error);
+    console.error('Development build failed:', error instanceof Error ? error.message : String(error));
     process.exit(1);
   }
 };
apps/obsidian/src/index.ts (1)

24-26: Add type assertion for loaded data

The loadData() method returns any, which could lead to runtime errors if the loaded data doesn't match the Settings type.

 async loadSettings() {
-  this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
+  const loadedData = await this.loadData() as Partial<Settings>;
+  this.settings = { ...DEFAULT_SETTINGS, ...loadedData };
 }
apps/obsidian/src/components/SampleModal.tsx (2)

6-7: Remove empty className props

Empty className props don't serve any purpose and should be removed.

-    <div className="">
+    <div>
       <h2 className="">SO RED</h2>
       <p>Lorum ipsum dolor sit amet.</p>
       <button
-        className=""
+        className="clickable-icon"
         onClick={() => {

Also applies to: 10-10


21-24: Remove unnecessary constructor

The constructor doesn't add any functionality beyond the parent constructor.

-  constructor(app: App) {
-    super(app);
-  }
🧰 Tools
🪛 Biome (1.9.4)

[error] 22-24: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

apps/obsidian/src/components/Settings.tsx (2)

9-11: Improve error message for missing context

The current error message is not descriptive enough. Consider providing more context about what went wrong.

   if (!app) {
-    return <div>An error occured</div>;
+    return <div>Error: Obsidian app context not found. This component must be used within an AppContext.Provider.</div>;
   }

45-53: Add debounce to settings onChange handler

The current implementation saves settings on every keystroke, which could lead to performance issues with rapid typing.

+ import { debounce } from "obsidian";
+
  addText((text) =>
    text
      .setPlaceholder("Enter your secret")
      .setValue(this.plugin.settings.mySetting)
-     .onChange(async (value) => {
+     .onChange(debounce(async (value) => {
        this.plugin.settings.mySetting = value;
        await this.plugin.saveSettings();
-     }),
+     }, 500)),
  );
apps/obsidian/src/utils/registerCommands.ts (1)

10-12: Add error handling for modal creation.

Both modal commands should include try-catch blocks to handle potential initialization errors.

 callback: () => {
+  try {
     new SampleModal(plugin.app).open();
+  } catch (error) {
+    console.error('Failed to open sample modal:', error);
+  }
 },

Also applies to: 36-38

apps/obsidian/.env.example (1)

1-1: Enhance environment variable documentation.

Consider adding:

  1. Platform-specific path examples (Windows, macOS, Linux)
  2. Instructions for finding the Obsidian plugins folder
  3. A note about copying this file to .env
-# OBSIDIAN_PLUGIN_PATH="path/to/your/obsidian/plugins/folder"
+# Copy this file to .env and update the path to your Obsidian plugins folder
+# Examples:
+# Windows: OBSIDIAN_PLUGIN_PATH="C:/Users/USERNAME/AppData/Roaming/obsidian/plugins"
+# macOS: OBSIDIAN_PLUGIN_PATH="/Users/USERNAME/Library/Application Support/obsidian/plugins"
+# Linux: OBSIDIAN_PLUGIN_PATH="/home/USERNAME/.config/obsidian/plugins"
apps/obsidian/manifest.json (1)

1-10: Add recommended manifest fields.

Consider adding these recommended fields to enhance plugin discoverability and maintenance:

  • fundingUrl: For sponsorship/donation links
  • repository: Link to the GitHub repository
  • keywords: For better search visibility
 {
   "id": "@discourse-graph/obsidian",
   "name": "Discourse Graph",
   "version": "0.1.0",
   "minAppVersion": "1.7.0",
   "description": "Discourse Graph Plugin for Obsidian",
   "author": "Discourse Graphs",
   "authorUrl": "https://discoursegraphs.com",
-  "isDesktopOnly": false
+  "isDesktopOnly": false,
+  "fundingUrl": "https://github.com/sponsors/DiscourseGraphs",
+  "repository": "https://github.com/DiscourseGraphs/discourse-graph",
+  "keywords": ["discourse", "graph", "knowledge-management"]
 }
apps/obsidian/README.md (2)

1-5: Enhance documentation with additional sections.

While the overview is good, consider adding essential sections:

  • Installation instructions
  • Basic usage guide
  • Development setup
  • Contributing guidelines

3-3: Consider breaking down the long sentence for better readability.

The description is contained in a single long sentence. Consider breaking it down into multiple sentences for clarity.

-The Discourse Graph extension enables Roam users to seamlessly add additional semantic structure to their notes, including specified page types and link types that model scientific discourse, to enable more complex and structured knowledge synthesis work, such as a complex interdisciplinary literature review, and enhanced collaboration with others on this work.
+The Discourse Graph extension enables Roam users to seamlessly add semantic structure to their notes. Users can specify page types and link types that model scientific discourse. This structured approach enables complex knowledge synthesis work, such as interdisciplinary literature reviews, and enhances collaboration.
apps/obsidian/package.json (1)

15-23: Consider updating TypeScript-related dependencies.

The TypeScript and related packages are using older versions. Consider updating to more recent versions for better compatibility and features.

  "devDependencies": {
-   "@types/node": "^16.11.6",
+   "@types/node": "^20.10.0",
-   "@typescript-eslint/eslint-plugin": "5.29.0",
-   "@typescript-eslint/parser": "5.29.0",
+   "@typescript-eslint/eslint-plugin": "^6.13.0",
+   "@typescript-eslint/parser": "^6.13.0",
    "builtin-modules": "3.3.0",
    "esbuild": "0.17.3",
    "obsidian": "^1.7.2",
-   "tslib": "2.4.0",
+   "tslib": "^2.6.2",
    "tsx": "^4.19.2",
-   "typescript": "4.7.4"
+   "typescript": "^5.3.0"
README.md (2)

72-75: Enhance Obsidian setup instructions.

Consider adding more details to the Obsidian setup:

  • Specify where to find the plugins folder on different operating systems
  • Add steps to enable the plugin in Obsidian after installation
  • Include troubleshooting steps

12-12: Strengthen the wording.

Consider using "accelerates" instead of "speeds up" for stronger wording.

-Only changes in code are rebuilt, which speeds up development.
+Only changes in code are rebuilt, which accelerates development.
🧰 Tools
🪛 LanguageTool

[style] ~12-~12: Consider using a different verb to strengthen your wording.
Context: ...Only changes in code are rebuilt, which speeds up development. - Parallel processing:...

(SPEED_UP_ACCELERATE)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c373c3 and 3b4d16a.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (18)
  • README.md (2 hunks)
  • apps/obsidian/.env.example (1 hunks)
  • apps/obsidian/README.md (1 hunks)
  • apps/obsidian/manifest.json (1 hunks)
  • apps/obsidian/package.json (1 hunks)
  • apps/obsidian/scripts/build.ts (1 hunks)
  • apps/obsidian/scripts/compile.ts (1 hunks)
  • apps/obsidian/scripts/dev.ts (1 hunks)
  • apps/obsidian/src/components/AppContext.tsx (1 hunks)
  • apps/obsidian/src/components/SampleModal.tsx (1 hunks)
  • apps/obsidian/src/components/Settings.tsx (1 hunks)
  • apps/obsidian/src/index.ts (1 hunks)
  • apps/obsidian/src/styles/styles.css (1 hunks)
  • apps/obsidian/src/utils/registerCommands.ts (1 hunks)
  • apps/obsidian/tsconfig.json (1 hunks)
  • apps/roam/tsconfig.json (0 hunks)
  • packages/typescript-config/base.json (1 hunks)
  • turbo.json (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/roam/tsconfig.json
🧰 Additional context used
🪛 Biome (1.9.4)
apps/obsidian/src/components/SampleModal.tsx

[error] 22-24: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

🪛 LanguageTool
README.md

[style] ~12-~12: Consider using a different verb to strengthen your wording.
Context: ...Only changes in code are rebuilt, which speeds up development. - Parallel processing:...

(SPEED_UP_ACCELERATE)

🔇 Additional comments (11)
apps/obsidian/tsconfig.json (2)

2-2: Configuration already follows the recommended base.

The configuration already extends @repo/typescript-config/react-library.json as suggested in the past review comment.


3-16: Verify compiler options against the base configuration.

Some of these compiler options might be redundant if they're already defined in @repo/typescript-config/react-library.json. Consider removing duplicate options to maintain a cleaner configuration.

✅ Verification successful

The compiler options are appropriately configured for Obsidian's requirements

While some options overlap with the base configuration, the differences in values (e.g., ES6 vs ES2022, ESNext vs NodeNext) and Obsidian-specific options (e.g., inlineSourceMap, inlineSources) appear intentional and necessary for Obsidian's environment. These aren't redundant configurations but rather purposeful overrides.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the base TypeScript configuration to identify duplicate options

# Find and display the base configuration file
echo "Searching for the base configuration file..."
fd -t f "react-library.json" -x cat {}

Length of output: 275


Script:

#!/bin/bash
# Find and display the base.json configuration file
echo "Searching for base.json..."
fd -t f "base.json" -x cat {}

Length of output: 681

packages/typescript-config/base.json (2)

7-7: LGTM: Standardizing module import behavior.

Moving allowSyntheticDefaultImports to the base configuration standardizes how default imports are handled across all packages. This is a good practice for maintaining consistency.


4-6: Verify that all packages follow the expected directory structure.

The paths mapping assumes all packages using this base config have a src directory. Please ensure this is the intended behavior and all packages follow this structure.

✅ Verification successful

The paths mapping configuration is safe and flexible

The current setup works well because:

  • Packages that need src-based imports (like ui) can override the paths
  • Configuration packages that work from root directory (like tailwind-config) include "." making the src mapping non-impactful
  • Other packages don't extend the TypeScript configuration
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if all packages have a src directory
echo "Checking for src directories in all packages..."
fd --type d '^src$' packages/ --exec echo "Found src directory in: {//}"

# List all package.json files to compare against
echo -e "\nFound packages:"
fd package.json packages/ --exec echo "{//}"

Length of output: 411


Script:

#!/bin/bash
# Check which packages extend the base typescript config
echo "Checking for tsconfig.json files and their content:"
fd tsconfig.json packages/ --exec sh -c 'echo "\nFile: {}"; cat {}'

Length of output: 598

apps/obsidian/scripts/compile.ts (2)

110-121: Incorporate CSS in your build entry points.
A TODO was mentioned regarding including CSS in entryPoints and using Tailwind. If desired, you can bundle styles with esbuild directly by specifying .css files as entry points or using a plugin. This manual approach is fine, but bundling could simplify the pipeline.

 builder({
   absWorkingDir: process.cwd(),
-  entryPoints: [path.join(srcRoot, entryTs)],
+  entryPoints: [
+    path.join(srcRoot, entryTs),
+    // Include CSS entry if needed:
+    // path.join(stylesDir, "main.css"),
+  ],
   outdir,
   ...

138-164: Avoid self-mirroring or recursive copies.
When mirror overlaps with your outdir, the mirror plugin copies compiled files from the output to the same output path, potentially causing repetition or overwriting. Consider detecting if mirror === outdir or if mirror is within outdir to prevent unintended side effects.

apps/obsidian/src/components/AppContext.tsx (1)

4-5: Provide fallback or error handling for undefined context.
When useApp() returns undefined, any consumers may cause a runtime error if they assume a valid App is always present. Consider returning a default object or throwing a clear error if AppContext is not provided.

apps/obsidian/src/index.ts (1)

22-22: Implement cleanup in onunload

The onunload method is empty. Consider implementing cleanup logic for registered commands and settings.

✅ Verification successful

Empty onunload() is acceptable

The current implementation doesn't require explicit cleanup as:

  • Commands are automatically unregistered by Obsidian's plugin system
  • Settings tab is automatically cleaned up
  • No event listeners or persistent resources are found that would require manual cleanup
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if there are any resources that need cleanup
rg -l "registerEvent|addCommand|addSettingTab" apps/obsidian/src/

Length of output: 138


Script:

#!/bin/bash
# Check the actual registration calls and their context
echo "=== Content of registerCommands.ts ==="
cat apps/obsidian/src/utils/registerCommands.ts

echo -e "\n=== Relevant sections from index.ts ==="
rg -A 5 -B 5 "registerEvent|addCommand|addSettingTab" apps/obsidian/src/index.ts

Length of output: 2201

apps/obsidian/manifest.json (1)

2-2: Review the plugin ID format.

The current ID uses an npm-style scoped package name (@discourse-graph/obsidian). Consider using a simpler, reverse-domain format as recommended by Obsidian (e.g., com.discoursegraphs.obsidian).

turbo.json (1)

18-18: LGTM!

The addition of OBSIDIAN_PLUGIN_PATH to passThroughEnv is appropriate for Obsidian plugin development.

README.md (1)

5-16: LGTM!

The Turborepo section is well-written and provides clear benefits of using the build system.

🧰 Tools
🪛 LanguageTool

[style] ~12-~12: Consider using a different verb to strengthen your wording.
Context: ...Only changes in code are rebuilt, which speeds up development. - Parallel processing:...

(SPEED_UP_ACCELERATE)

Comment on lines +83 to +85
const stylesDir = path.join(root, "src", "styles");
const outputStylesFile = path.join(outdir, "styles.css");

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Gracefully handle missing styles directory.
This script expects a styles directory to exist. If the directory is absent, an error occurs when reading style files. Consider adding a guard check or fallback behavior when stylesDir is missing to avoid build-time exceptions.

handle SIGINT and SIGTERM signals

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@mdroidian mdroidian merged commit 6762e0f into main Jan 27, 2025
4 checks passed
@mdroidian mdroidian deleted the obsidian-init branch January 27, 2025 03:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

No open projects
Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants