Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c3034d6
Disabled debug button
Robert35-dll Jul 8, 2025
c8f0edf
Renamed nodes' types
Robert35-dll Jul 8, 2025
cacf810
Added type updating upon loading mind maps
Robert35-dll Jul 8, 2025
db0177c
Swapped Open / Save buttons.
Robert35-dll Jul 8, 2025
4cadef5
Renamed popups with BibEntry to BibTex
Robert35-dll Jul 8, 2025
4eb8aa5
Replaced disabling dropdown buttons
Robert35-dll Jul 9, 2025
de04f16
Update README.md
iloveskittles82 Jul 9, 2025
bf7ccc8
add shortcut cheat sheet
iloveskittles82 Jul 9, 2025
4d232bb
add link to shortcuts cheat sheet
iloveskittles82 Jul 9, 2025
5dea19f
Add ctrl + S for saving and added gg.cmd to gitignore
iloveskittles82 Jul 9, 2025
068e8d5
Add CTRL + S
iloveskittles82 Jul 9, 2025
a8a973a
Reworked disabling dropdowns' buttons
Robert35-dll Jul 9, 2025
279a155
Sync merge branch 'finishing-sprint-3' of https://github.com/JabRef/j…
Robert35-dll Jul 9, 2025
a330f7e
Implemented ModalDesigner
Robert35-dll Jul 11, 2025
cf2c3d4
Extracted default mind maps and options
Robert35-dll Jul 11, 2025
96dfbcb
Fixed minor bugs with extracted options
Robert35-dll Jul 12, 2025
b6a44c4
Refactored HTTPClient default return value
Robert35-dll Jul 12, 2025
da7e6ed
Cleaned HTTPClient
Robert35-dll Jul 12, 2025
d4c5539
Moved rest of buttons into "General Manipulation" region
Robert35-dll Jul 12, 2025
c8dbf20
Cleaned main.js
Robert35-dll Jul 12, 2025
c2bedc3
Added more shortcuts
Robert35-dll Jul 12, 2025
469ff95
Added cutting long nodes' topics
Robert35-dll Jul 12, 2025
fafdd99
Created a "Connection Failed" page
Robert35-dll Jul 12, 2025
d3348b0
Applied suggestions to README.md
Robert35-dll Jul 14, 2025
6c159aa
Added shortcuts documentation
Robert35-dll Jul 14, 2025
53478c0
fixed comments
iloveskittles82 Jul 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,6 @@ electron-dist/
/.idea/modules.xml
/.idea/vcs.xml
/JabMap/.idea/

# wrapper tool for trying out jabmap
/gg.cmd
92 changes: 72 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,81 @@
# JabMap

Next-Generation scientific mind mapping.


## 📸 Screenshots
<img width="1548" alt="JabMap mainview example" src="https://github.com/user-attachments/assets/69f5de97-3b2d-4ed7-b8f8-050a1559f93b" />
<img width="1549" alt="JabMap opening a mindmap example" src="https://github.com/user-attachments/assets/23aa56d0-4432-4e5f-957b-8797d36a22fd" />

<img width="1429" alt="JabMap-Example" src="https://github.com/user-attachments/assets/4a175c10-a0eb-438f-b5f6-1dcfb4e2520c" />


## ⌨️ Shortcuts

You can find a list of shortcuts at [shortcuts.md](shortcuts.md).

## 🌟 Try It Out!
The current state of the application is hosted on [github pages](https://jabref.github.io/jabmap/) for you to try out. Note that saving and loading mind maps does not work when running the app like this because the communication with the [JabRef's HTTP server](#getting-the-server) is restricted by your browser for security reasons. Unfortunately this applies to any interaction with the server.

## 💾 Installation
There are a couple of ways to try out JabMap. The fastest way to just get a grasp of it is on [github pages](https://jabref.github.io/jabmap/). Note that this will merely allow you to create some nodes, edit and tag them. Saving, Loading and JabRef-related features like BibTeX-Nodes and importing attached PDF-files as nodes won't work unless you are running our version of JabRef's HTTP-Server (see [below](##-🤖-getting-the-server-running) for setup) since the mentioned features rely on it.

>Note: It's still possible your browser blocks access to the server due to HTTP/HTTPS issues, for the best experience follow the steps below on how to try JabMap running it locally.

### Running it locally

The following commands get the code in place and start JabRef and JabMap with the help of a handy wrapper tool called [gg.cmd](https://github.com/eirikb/gg).
A little terminal magic is required, but don't worry, we have the commands all laid out for you!

#### JabRef:

1. Go to your git-repositories folder and start a new terminal session
2. `git clone --recurse-submodules https://github.com/JabRef/jabref.git`
3. `cd jabref`
4. `git checkout jabmap`
5. Enable nice wrapper: `curl -L ggcmd.io > gg.cmd`
6. `sh ./gg.cmd just run-pr 13519`
7. Wait for JabRef to come up
8. File > Preferences > Check "HTTP Server"

#### JabMap:

1. Go to your git-repositories folder and start a new terminal session
2. Clone it: `git clone git@github.com:JabRef/jabmap.git`
3. `cd jabmap`
4. Fix branch: `git checkout jabmap`
5. Enable nice wrapper: `curl -L ggcmd.io > gg.cmd`
6. Install dependencies: `sh gg.cmd npm install`
7. Build: `sh ./gg.cmd npm run build`
8. Run: `sh ./gg.cmd npm run preview`
9. Now one can open http://localhost:4173/ and open a library with the corresponding map.


## 🤖 Getting the server running

As mentioned above, several features are handled by JabRef's HTTP server. Currently you have to start it manually. Luckily, there are multiple ways to do that:

### using gg.cmd

If you followed the steps (**running-it-locally**) above, instead of steps 6. - 9. you can simply do the following to start the server without JabRef's GUI:

```
sh ./gg.cmd jbang .jbang/JabSrvLauncher.java
```

### Starting it from an IDE

1. If you haven't already, clone our [JabRef's fork repository](https://github.com/iloveskittles82/jabref) (_Note: It is recommended to complete this step of_ [_JabRef's setup guide_](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.html)).
2. Afterwards, open it in editor of your choice (_IDEA works well for this_) and locate the `jabsrv/src/test/rest-api.http` file.
3. Follow the steps described at the top of the file to start the server.

_Alternatively_ you can do the following:
1. Open `ServerCLI` file located at `./jabsrv-cli/src/main/java/org.jabref.http.server.cli`
2. Execute its `main()` method

More about starting the server in [JabRef's server documentation](https://devdocs.jabref.org/code-howtos/http-server.html)


## 💾 Install by building the application

Now that you've tried it, here is how to build and install it. We hope to provide a downloadable installer in the future, but for now you have to build the app yourself.
This section is more targeted towards
Currently, there is no production build available for download so you have to build it yourself :3

### Basic Setup
Expand Down Expand Up @@ -39,24 +106,9 @@ If bundling fails with `ERROR: Cannot create symbolic link`, you have to do the

_More about this workaround in [this issue](https://github.com/electron-userland/electron-builder/issues/8149)_.

On Linux, there is a bug with npm and optional dependencies (See [this issue](https://github.com/npm/cli/issues/4828). Should you encounter this bug after running `npm install`, remove the `package-lock.json` file and `node-modules` directory and run `npm i`. Then, simply continue with step 2.
On Linux and Mac, there is a bug with npm and optional dependencies (See [this issue](https://github.com/npm/cli/issues/4828). Should you encounter this bug after running `npm install`, remove the `package-lock.json` file and `node-modules` directory and run `npm i`. Then, simply continue with step 2.

### Starting
After a successful build you can finally start the app located at `./electron-dist/win-unpacked/JabMap.exe`.

_Optionally you can install it by opening_ `./electron-dist/JabMap Setup 1.0.0.exe`

## 🤖 Getting the server running
As mentioned above, several features are handled by JabRef's HTTP server. Currently you have to start it manually:

First clone our [JabRef's fork repository](https://github.com/iloveskittles82/jabref) (_Note: It is recommended to complete this step of_ [_JabRef's setup guide_](https://devdocs.jabref.org/getting-into-the-code/guidelines-for-setting-up-a-local-workspace/intellij-12-build.html)).

After you cloned the repository, open it in editor of your choice (_IDEA works well for this_) and locate the `jabsrv/src/test/rest-api.http` file.

Follow the steps described at the top of the file to start the server.

_Alternatively_ you can do the following:
1. Open `ServerCLI` file located at `./jabsrv-cli/src/main/java/org.jabref.http.server.cli`
2. Execute its `main()` method

More about starting the server in [JabRef's server documentation](https://devdocs.jabref.org/code-howtos/http-server.html)
119 changes: 83 additions & 36 deletions http/HTTPClient.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
/**
* Provides HTTP-connection functionality.
*/
export class HTTPClient {
class HTTPClient {
#host = "http://localhost:23119/";

constructor() {
/**
* The default return value for PUT requests.
*/
this.NULL_MAP = { map: {} };
this.currentLibrary = "demo";
}

/**
* Sends a HTTP-request to JabRef's server.
* Sends an HTTP-request to JabRef's server.
* @param { string } url - The server's URL to make a request to.
* @param { object } options - Optional request's options.
* @returns
* - An **object** in case of a `GET request` or
* - An **object** { map: {} } in case of a `PUT / POST request`
* or if any request failed.
* @returns A result object of following structure:
* - code: the status of the request defined by REQUEST_RESULT enum.
* - value: **null** in case of `PUT` or `POST` requests
* or **object** requested by `GET`.
*/
async #performRequest(url, options = null) {
let result = this.NULL_MAP;
let result = {
code: REQUEST_RESULT.Refused,
value: null
};
let logMessage = "";

const requestUrl = this.#host.concat(url);
Expand All @@ -41,19 +40,25 @@ export class HTTPClient {
throw new Error("Request's result is not ok ( -.-)");
}

// If connection succeeded, try parsing the result (failed by default)
result.code = REQUEST_RESULT.Failed;

// If some output is awaited, save it
if (options.method !== "PUT") {
if (options.headers["Accept"] === 'application/json') {
result = await response.json();
result.value = await response.json();
}
if (options.headers["Accept"] === 'text/plain') {
result = await response.text();
result.value = await response.text();
}
if (options.headers["Accept"] === 'text/html') {
result = await response.text();
result.value = await response.text();
}
}

// If parsing's successful, set the best result code \(OwO)/
result.code = REQUEST_RESULT.Success;

// Providing infos about the request
logMessage =
`${options.method} ${requestUrl} Request succeeded (~ UwU)~(${response.status}).\n` +
Expand Down Expand Up @@ -85,7 +90,7 @@ export class HTTPClient {
*/
async isConnected() {
try {
const response = await fetch(this.#host);
await fetch(this.#host);
return true;
} catch (e) {
return false;
Expand All @@ -95,7 +100,10 @@ export class HTTPClient {
/**
* Requests a mind map (.jmp file) from JabRef's server.
* @param { string } library - The library of the requested mind map.
* @returns The requested mind map object.
* @returns A result object of following structure:
* - code: the status of the request defined by REQUEST_RESULT enum.
* - value: null in case the request failed
* or requested mind map.
*/
async loadMap(library = "demo") {
const url = `libraries/${library}/map`;
Expand All @@ -104,17 +112,23 @@ export class HTTPClient {
headers: { "Accept": "application/json" }
}

// Changing current library
this.currentLibrary = library;
console.log(`Current library is now: ${this.currentLibrary}`);
const loadRequest = await this.#performRequest(url, options);

return this.#performRequest(url, options);
// Changing current library if succeeded
if (loadRequest.code === REQUEST_RESULT.Success) {
this.currentLibrary = library;
console.log(`Current library is now: ${this.currentLibrary}`);
}

return loadRequest
}

/**
* Sends a mind map to JabRef's server to save next to currently active library.
* @param { object } mindMap - The mind map to save.
* @returns An empty map object (NULL_MAP).
* @returns A result object of following structure:
* - code: the status of the request defined by REQUEST_RESULT enum.
* - value: null.
*/
async saveMap(mindMap) {
const url = `libraries/${this.currentLibrary}/map`;
Expand All @@ -129,7 +143,10 @@ export class HTTPClient {

/**
* Requests a list of stored mind maps saved on the server.
* @returns A list of available mind maps stored on the server.
* @returns A result object of following structure:
* - code: the status of the request defined by REQUEST_RESULT enum.
* - value: null in case the request failed
* or a list of available mind maps.
*/
async listMaps() {
const url = 'libraries'
Expand All @@ -143,7 +160,10 @@ export class HTTPClient {

/**
* Requests a list of all entries in the current library.
* @returns A list of all entries in the current library in json format.
* @returns A result object of following structure:
* - code: the status of the request defined by REQUEST_RESULT enum.
* - value: null in case the request failed
* or a list of available entries of current mind map.
*/
async listEntries() {
const options = {
Expand All @@ -156,7 +176,10 @@ export class HTTPClient {
/**
* Sends a request to open a cite-as-you-write window
* to select any number of BibEntries from the current library.
* @returns A list of selected citation keys.
* @returns A result object of following structure:
* - code: the status of the request defined by REQUEST_RESULT enum.
* - value: null in case the request failed
* or list of requested citation keys.
*/
async getCiteKeysWithCAYW() {
let url = 'better-bibtex/cayw';
Expand All @@ -166,19 +189,30 @@ export class HTTPClient {
method: "GET",
headers: { "Accept": "application/json" }
}
let selectedEntries = await this.#performRequest(url, options);
let selectedCiteKeys = [];
for (let entry of selectedEntries) {
selectedCiteKeys.push(entry.citationKey);

let entriesRequest = await this.#performRequest(url, options);
let selectedEntries = entriesRequest.value;

if (entriesRequest.code !== REQUEST_RESULT.Success) {
return null;
}

let selectedCiteKeys = [];
for (let entry of selectedEntries) {
selectedCiteKeys.push(entry.citationKey);
}

return selectedCiteKeys;
}

/**
* Requests the preview for a certain BibEntry from the current library.
* @param { string } citationKey - The citation key (identifier) of the entry.
* @returns A string containing the preview with relevant information
* about the entry (e.g. author, title, release date, etc.).
* @returns A result object of following structure:
* - code: the status of the request defined by REQUEST_RESULT enum.
* - value: null in case the request failed
* or a string containing the preview with relevant information
* about the entry (e.g. author, title, release date, etc.).
*/
async getPreviewString(citationKey) {
const url = `libraries/${this.currentLibrary}/entries/${citationKey}`;
Expand All @@ -193,8 +227,11 @@ export class HTTPClient {
/**
* Requests the preview for a certain BibEntry from the current library.
* @param { string } citationKey - The citation key (identifier) of the entry.
* @returns A string containing the preview with relevant information
* about the entry (e.g. author, title, release date, etc.).
* @returns A result object of following structure:
* - code: the status of the request defined by REQUEST_RESULT enum.
* - value: null in case the request failed
* or an HTML string containing the preview with relevant information
* about the entry (e.g. author, title, release date, etc.).
*/
async getPreviewHTML(citationKey) {
const url = `libraries/${this.currentLibrary}/entries/${citationKey}`;
Expand All @@ -208,7 +245,10 @@ export class HTTPClient {

/**
* Requests a list of all local pdf files from the current library.
* @returns A list of objects of the following structure
* @returns A result object of following structure:
* - code: the status of the request defined by REQUEST_RESULT enum.
* - value: null in case the request failed
* or a list of objects of the following structure
* ```
* [
* {
Expand All @@ -229,7 +269,14 @@ export class HTTPClient {
headers: { "Accept": "application/json" }
}

let response = await this.#performRequest(url, options);
return response;
return this.#performRequest(url, options);
}
}
}

const REQUEST_RESULT = Object.freeze({
Refused: -1,
Failed: 0,
Success: 1
});

export { HTTPClient, REQUEST_RESULT };
Loading