By: Team Notably Since: Feb 2020 Licence: MIT
1. Setting up
Refer to the guide here.
2. Design
The .puml files used to create diagrams in this document can be found in the diagrams folder.
Refer to the Using PlantUML guide to learn how to create and edit diagrams.
|
2.1. Architecture
2.1.1. Design pattern and data flow
The App is built following the Model-View-Controller design pattern.
In addition, the App’s data flow is unidirectional. That is, all user interactions in View will trigger an appropriate handler in Logic, which in turn updates Model and Storage.
Any data/state changes in Model will then propagate back to View automatically through JavaFX’s Property and Binding. In other words, the Observer design pattern is employed; Model is the observable while View is the observer.
2.1.2. Architecture-level components
Overall, the App consists of five main components:
-
Defines its API in an
interfacewith the same name as the itself, e.g.Logic.java -
Exposes its functionality using a
{Component Name}Managerclass, e.g.LogicManager.java
The following classes from Commons plays an important role at the architecture level:
2.1.3. How the architecture components interact with each other
The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command new -t Notably.
new -t Notably command2.2. View
API : View.java
The View consists of a MainWindow that is made up of parts e.g.CommandBox, SuggestionsList, SideBarTree, HelpModal, BlockContent etc. All these, including the MainWindow, inherit from the abstract ViewPart class.
The View component uses JavaFx framework. The layout of these View parts are defined in matching .fxml files that are in the src/main/resources/view folder. For example, the layout of the MainWindow is specified in MainWindow.fxml
The View component,
-
Executes user commands using the
Logiccomponent. -
Listens for changes to
Modeldata so that the View can be updated with the modified data.
2.3. Logic
API :
Logic.java
Logic consists of 3 subcomponents:
-
NotablyParser: Main parser of the App, deals with user command execution. -
SuggestionEngine: Deals with suggestions generation. -
CorrectionEngine: Deals with auto-correction.
2.3.1. NotablyParser component
-
Logicuses theNotablyParserclass to parse the user command. -
This results in a
List<Command>object which is executed by theLogicManager. -
The command execution can affect the
Model(e.g. adding a Note). -
The updated model/data structure will automatically be reflected on to the
View.
Given below is the Sequence Diagram for interactions within the Logic component for the execute("delte -t cs2103") API call.
delte -t cs2103 Command
The lifeline for DeleteCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
|
2.3.2. SuggestionEngine
API :
SuggestionEngine.java
SuggestionEngine gives users the meaning of the command they input and a list of notes suggestions that they want to
open, delete, or search.
-
Logicuses theSuggestionEngineclass, to handle the user input. -
According to the command the user inputs,
SuggestionEnginewill create aXYZSuggestionArgHandlerorABCSuggestionHandlerobject which implementsSuggestionArgHandlerandSuggestionHandlerinterface respectively.XYZSuggestionArgHandlerare for commands that require argument parsing, i.e.open,delete,search,new, whereasABCSuggestionHandlerare for commands that do not require argument parsing, i.e.edit,exit,help. -
If
SuggestionArgHandlerobject is created: theresponseTextin theModelwill be updated. This case will also result in the creation ofXYZSuggestionGeneratorobject (except fornewcommand) which implementsSuggestionGeneratorinterface.XYZSuggestionGeneratoris then executed by theSuggestionEngine. -
If
SuggestionHandlerobject is created: theresponseTextin theModelwill be updated. -
The
Modelcould be affected in 2 ways:-
Update
responseTextof theModel(by theSuggestionHandlerandSuggestionArgHandler): for instance, the inputopen /will set theresponseTextin theModelas "Open a note". -
Store a list of
SuggestionItemin theModel(by theSuggestionGenerator).
-
-
The UI will then be able to retrieve the
responseTextand list ofSuggestionItemfrom theModelto be displayed to the user.
Given below is the Sequence Diagram for interactions within the Logic and Suggestion component for the input opne /a.
opne /a
The lifeline for OpenSuggestionArgHandler should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
|
2.3.3. CorrectionEngine
The CorrectionEngine component revolves around two API s, namely:
-
The
CorrectionEngineinterface, implemented byStringCorrectionEngineandAbsolutePathCorrectionEngine. Concrete implementations ofCorrectionEngineare employed to correct an uncorrected user input. -
The
EditDistanceCalculatorinterface, implemented byLevenshteinDistanceCalculator. Concrete implementations ofEditDistanceCalculatorare employed to calculate the edit distance between two strings.
Given below is the Sequence Diagram for interactions within the StringCorrectionEngine (one concrete implementation of CorrectionEngine) component for the correct("uncorrected") API call.
correct("uncorrected") call2.4. Model
API : Model.java
The Model,
-
stores and manipulates the
BlockTreedata that represents a tree of Blocks, through BlockModel -
stores and manipulates a list of suggestions based on the user’s input, through SuggestionModel
-
stores the current state of the
View, through ViewStateModel-
stores the command input given by the user, through CommandInputModel
-
stores the state of the
helpmodal being open, through HelpFlagModel -
stores the state of the current block’s
editmodal being open, through BlockEditFlagModel
-
-
stores
UserPrefdata that represents the user’s preferences, through UserPrefModel
2.4.1. BlockModel component
API : BlockModel.java
The BlockModel
-
stores and directly manipulates the
BlockTree-
contains a single
BlockTreeItemas theroot; the tree is built by adding chilrenBlockTreeItemsto theroot-
each
BlockTreeItemstores the reference to its parent and childrenBlockTreeItems, and its own content, throughTreeItem<Block>-
stores its content, through
Block-
contains the
TitleandBodycontent
-
-
-
-
2.5. Storage
API : Storage.java
The Storage component,
-
can save
UserPrefobjects in JSON format and read it back. -
can save Notably’s
BlockModeldata in JSON format and read it back.-
stores the
BlockTreeand also the path of the last openedBlock
-
2.6. Commons
Classes used by multiple components are in the com.notably.commons package.
2.7. Compiler
The Compiler component’s primary usage is to compile Markdown to HTML.
Our Compiler component’s design is based off the parsing strategy explained in GitHub’s GFM Specification
Mainly, the Compiler component consists of the following classes:
-
Compiler, which deals with the end-to-end job of compiling unprocessed Markdown to HTML. -
Parser, which deals with creating an Abstract Sytax Tree representation of an unprocessed Markdown. -
Block, which is an abstract class representing a node in a Markdown Abstract Syntax Tree. All concrete implementations of nodes in a Markdown Abstract Syntax Tree inherit from this class.
The concrete implementations of the Block class consist of:
-
DocumentBlock, which represents the root of the Markdown Abstract Syntax Tree. -
HeaderBlock, which represents a Markdown ATX heading component. -
ListBlock, which represents a Markdown unordered list. -
ListItemBlock, which represents a Markdown list item. -
ParagraphBlock, which represents a Markdown paragraph. -
TextBlock, which represents plain text in Markdown.
Two of Block's abstract methods are particularly important:
-
Block#next: This method should be implemented by each ofBlock's implementation in such a way that accepts a singleStringline and evolve the current Markdown Abstract Syntax Tree further. That way, each ofBlock's implementation only needs to care about processing the portion of theStringline that is relevant to them, before delegating the rest to its childrenBlocks. -
Block#toHtml: This method should be implemented by each ofBlock's implementation in such a way that it returns the HTML representation of the currentBlock. That way, each ofBlock's implementation only needs to care about generating its own HTML; the rest can be delegated to its childrenBlocks.
In short, our Compiler class will first call the Parser#parse method to generate a Markdown Abstract Syntax Tree.
After that, the Compiler class will transform the returned Markdown Abstract Syntax Tree into HTML by calling the root DocumentBlock's toHtml method (which will in turn invoke each of its children’s toHtml method).
Given below is the Sequence Diagram for interactions within the Compiler component for the compile(markdown) API call.
compile(markdown) call2.8. Logging
We are using java.util.logging package for logging. The LogsCenter class is used to manage the logging levels and logging destinations.
-
The logging level can be controlled using the
logLevelsetting in the configuration file (See Section 2.9, “Configuration”) -
The
Loggerfor a class can be obtained usingLogsCenter.getLogger(Class)which will log messages according to the specified logging level -
Currently log messages are output through:
Consoleand to a.logfile.
Logging Levels
-
SEVERE: Critical problem detected which may possibly cause the termination of the application -
WARNING: Can continue, but with caution -
INFO: Information showing the noteworthy actions by the App -
FINE: Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size
2.9. Configuration
Certain properties of the application can be controlled (e.g user prefs file location, logging level) through the configuration file (default: config.json).
3. Implementation
This section describes the details on how features are implemented.
3.1. Correction Engine
3.1.1. Rationale
CorrectionEngine is needed to enable auto-correction of user inputs, to deliver as good typing experience as possible.
3.1.2. Current implementation
CorrectionEngine revolves around two API s, namely:
-
The
CorrectionEngineinterface, implemented byStringCorrectionEngineandAbsolutePathCorrectionEngine. Concrete implementations ofCorrectionEngineare employed to correct an uncorrected user input. -
The
EditDistanceCalculatorinterface, implemented byLevenshteinDistanceCalculator. Concrete implementations ofEditDistanceCalculatorare employed to calculate the edit distance between two strings.
Two concrete implementations of the CorrectionEngine interface are, namely:
-
The
StringCorrectionEngineclass, which deals with the correction of plain strings. -
The
AbsolutePathCorrectionEngineclass, which deals with the correction of absolute paths. The absolute paths here refer to the address of the notes (or blocks, as we call it) that exist in the App.
3.1.3. Design considerations
-
CorrectionEngineis built as a standalone module that can be used by bothSuggestionEngineandParser. This decision is made so that code duplication in relation to auto-correction is minimal. -
Both
CorrectionEngineandEditDistanceCalculatorare implemented as interfaces, in an attempt to make the design of theCorrectionEnginecomponent resilient to change. This design enables us to leverage on the strategy pattern to make ourCorrectionEnginecomponent more future-proof.
3.2. Suggestion Engine
3.2.1. Rationale
SuggestionEngine allows the users to traverse their notes conveniently, without having
to remember the hierarchical structure of their notes. SuggestionEngine gives users the meaning of the command they input and a list of notes suggestions that they want to
open, delete, or search.
3.2.2. Current implementation
| SuggestionArgHandler | SuggestionHandler | |
|---|---|---|
Purpose |
Handles the arguments part of the user input and updates the |
Updates the |
Commands |
|
|
Suggestion Generation |
Yes, by |
No |
-
Logicuses theSuggestionEngineclass, to handle the user input. -
According to the command the user inputs,
SuggestionEnginewill create aXYZSuggestionArgHandlerorABCSuggestionHandlerobject which implementsSuggestionArgHandlerandSuggestionHandlerinterface respectively.XYZSuggestionArgHandlerare for commands that require argument parsing, i.e.open,delete,search,new, whereasABCSuggestionHandlerare for commands that do not require argument parsing, i.e.edit,exit,help. -
If
SuggestionArgHandlerobject is created: theresponseTextin theModelwill be updated. This case will also result in the creation ofXYZSuggestionGeneratorobject (except fornewcommand) which implementsSuggestionGeneratorinterface.XYZSuggestionGeneratoris then executed by theSuggestionEngine. -
If
SuggestionHandlerobject is created: theresponseTextin theModelwill be updated. -
The
Modelcould be affected in 2 ways:-
Update
responseTextof theModel(by theSuggestionHandlerandSuggestionArgHandler): for instance, the inputopen /will set theresponseTextin theModelas "Open a note". -
Store a list of
SuggestionItemin theModel(by theSuggestionGenerator).
-
-
The UI will then be able to retrieve the
responseTextand list ofSuggestionItemfrom theModelto be displayed to the user.
3.2.3. Design considerations
Aspect 1: Design with respect to the whole architecture
-
SuggestionEngineis segregated fromParserin order to differentiate the logic when the user has finished typing and pressed Enter (which will be handled byParser) in contrast to when the user presses the keyboard down button and Enter to take in the suggestion item. -
In order to keep the App’s data flow unidirectional,
SuggestionEnginewill update theresponseText(which tells the user the meaning of his command) and the list ofSuggestionIteminto theModel. Thus, by not showing theresponseTextand suggestions immediately to the UI,SuggestionEnginewill not interfere with theViewfunctionality. -
SuggestionArgHandler,SuggestionHandler,SuggestionGenerator,SuggestionItem, andSuggestionModelare implemented as interfaces, in an attempt to make the design of theSuggestionEnginecomponent resilient to change.
Aspect 2: Implementation of suggestions generation
-
Alternative 1: Have a
SuggestionCommandParserinterface andSuggestionCommandinterface to parse each of the command, updateresponseTextin theModel, and give suggestions.-
Pros: This provides a consistency for all the commands, where each command has a
XYZSuggestionCommandParserandXYZSuggestionCommandclass. -
Cons: The
SuggestionCommandParsersof the commands that do not require parsing of user input (edit,exit,help) end up passing auserInputargument that is not being used anywhere, which makes this design unintuitive. Moreover, since the updating of theresponseTextin theModelcan be done in eachSuggestionCommandParser, theSuggestionCommand`s of `edit,exit, andhelpend up to be redundant.
-
-
Alternative 2 (current choice): Create 2 separate interface to handle commands with input parsing and those without, and name it as a
SuggestionArgHandlerandSuggestionHandlerrespectively.-
Pros: This solves the cons discussed in Alternative 1, as this design gives a separate implementation for the commands with input parsing and those without. It does not force the
Handlerto parse the user input when there is no need to. The namingHandleralso does not restrict the functionality of the interface and classes to just parse an input, but allows for a flexibility in executing other functionality such as updating theresponseTextin theModel.
-
3.3. Correction Engine
3.3.1. Rationale
CorrectionEngine is needed to enable auto-correction of user inputs, to deliver as good typing experience as possible.
3.3.2. Current implementation
The CorrectionEngine component revolves around two API s, namely:
-
The
CorrectionEngineinterface, implemented byStringCorrectionEngineandAbsolutePathCorrectionEngine. Concrete implementations ofCorrectionEngineare employed to correct an uncorrected user input. -
The
EditDistanceCalculatorinterface, implemented byLevenshteinDistanceCalculator. Concrete implementations ofEditDistanceCalculatorare employed to calculate the edit distance between two strings.
Two concrete implementations of the CorrectionEngine interface are, namely:
-
The
StringCorrectionEngineclass, which deals with the correction of plain strings. -
The
AbsolutePathCorrectionEngineclass, which deals with the correction of absolute paths. The absolute paths here refer to the address of the notes (or blocks, as we call it) that exist in the App.
3.3.3. Design considerations
-
CorrectionEngineis built as a standalone module that can be used by bothSuggestionEngineandParser. This decision is made so that code duplication in relation to auto-correction is minimal. -
Both
CorrectionEngineandEditDistanceCalculatorare implemented as interfaces, in an attempt to make the design of theCorrectionEnginecomponent resilient to change. This design enables us to leverage on the strategy pattern to make ourCorrectionEnginecomponent more future-proof.
3.4. Paths
Given below is the implementation detail of the Path feature and some alternative design considerations.
3.4.1. Current Implementation
The Path interface represents the location of a Block in our data structure. A path can exist in 2 forms namely :
-
AbsolutePath
-
RelativePath
An AbsolutePath is a path that takes its reference from the root / block.
While a RelativePath takes it reference from the current note that is opened.
Currently the user is given the freedom to provide any of the 2 forms when using the open, delete command.
Given the following DataStructure below.
Using AbsolutePath open /CS2101 and using RelativePath open ../CS2101 would yield the same result.
3.4.2. Design Consideration
Aspect: Implementation of Path :
-
Alternative 1(Current choice): Have 2 separate class implementing
Path, which isAbsolutePathandRelativePath.-
Pros: More readable and OOP, each class can have their individual validity REGEX.
-
-
Alternative 2: Implement a single class
PathImpland have a boolean flagisAbsoluteto tell if its a Relative or Absolute path.
Aspect: Logical equivalence of RelativePath :
-
Alternative 1(Current choice): Relative path
CS2103/../note1would be equivalent tonote1.-
Pros: More intuitive for the user and developer, making it easier to integrate paths with other features.
-
-
Alternative 2: Relative path
CS2103/../note1would not be logically equivalent tonote1.
3.5. Tree Data Structure
Notably aims to provide end user a neat and well-organized workspace to store their notes. This is done by creating a tree structure; allowing users to create folder-like paths to organize their notes and group them into categories to their own liking.
3.5.1. Rationale
While this can be done with a linear data structure (a simple list), a linear list of notes would require more work to establish the relationship between groups of notes. A tree data structure supports this better, giving a clearer distinction while also establishing a form of hierarchy (as seen in the design example below).
On top of that, observability must be ensured so that the UI can update with any changes that happen on the tree (and its nodes) and also the data within each node.
3.5.2. Current Implementation
A custom tree data structure that supports observability has been implemented. As seen here, the BlockModel is the entry of point of manipulating the data tree. The tree (referred to as BlockTree) is made up of tree nodes (referred to as BlockTreeItem). The tree is observable such that if any change occurs on any of the tree’s nodes, the change event will bubble upwards to the root node. Hence, the root node serves as the entry point for the BlockTree.
To achieve this design, a BlockTreeItem needs to contain 3 primary components:
-
an Observable reference to its parent
-
an ObservableList of its children
-
User’s note data (referred to as
Blockdata) consisting of:-
Titleof the note -
Bodycontent of the note (optional)
-
After multiple designs, the current implementation now has BlockTreeItem using an underlying TreeItem<Block> to handle the general behaviour of a tree node.
When manipulating the BlockTree, the execution of any operation is always split in this order:
-
Get the
currentlyOpenPathfrom the BlockModel -
BlockModel carries out the command required based on that
currentlyOpenPath
An example of an operation is new -t CS2103T. To execute this, the following sequence occurs:
-
NewCommandParsercreates theBlockthat has the title 'CS2103T' and a default empty body.-
The
TitleandBodyobjects are created as well
-
-
NewCommandthen calls the Model and in turn, the BlockModel to add thisBlockto the BlockTree -
BlockModelfirst obtains thecurrentlyOpenPathto execute the operation on, i.e in this case, to add the newBlockon the path -
BlockModelcallsBlockTreeto add theBlockto theAbsolutePathobtained fromBlockModel -
BlockTreecreates aBlockTreeItemusing theBlockparsed earlier. -
Subsequently, the underlying
TreeItem<Block>is created and theBlockTreeItemis then added to the BlockTree.
Below is a sequence diagram that demonstrates this example
3.5.3. Design Considerations
Aspect: Using JavaFX’s TreeItem<T> vs implementing a BlockTreeItem from scratch
Current choice: Using JavaFX’s TreeItem<T>
Pros:
-
seamless integration with JavaFX’s
TreeViewwhich is used in Notably’s sidebar to show the notes in a traditional file-browser-like manner -
TreeItem<T>has the requirements ofBlockTreeItem’sdesign already implemented to a usable extent -
conveniently handles underlying event handling required for
BlockTreeto be observable
Cons:
-
Implementation still requires wrapping and unwrapping of underlying
TreeItem<T>to work withTreeView
Aspect: BlockTreeItem vs Folders to represent path structure
Current choice: BlockTreeItem
Pros:
-
No need for an additional class. Having a separate
folderobject would also require a separate UI View since folders should not contain any block data.
Cons:
-
Somewhat unconventional design. User might be unfamiliar with the intention on first use, without proper explanation
Aspect: Root should also be a BlockTreeItem
Pros:
-
Seamless transition to JSON storage
Cons:
-
Need to add constraint to ensure that the root
BlockTreeItemdoes not contain anyBodyand is also unmodifiable
3.6. Compiler
3.6.1. Rationale
Compiler is needed to enable compilation of Markdown to HTML. By having an Markdown to HTML compiler, we can allow user to format their notes in Markdown, which enhances their note-editing experience tremendously.
3.6.2. Current implementation
The implementation of Compiler is highly inspired by the parsing strategy explained in GitHub’s GFM Specification. Please read more from the specification for a more comprehensive explanation.
3.6.3. Design considerations
Generally speaking, compilers usually consist of several main components, namely a tokenizer, a parser, and a generator. However, this is not the case in our design of the Compiler component:
-
Leveraging on the fact that Markdown’s syntax is not overly complicated, we decided not to fully adhere to the usual compiler design. Instead, we merged the tokenizer and parser section into our
Parserclass. ThisParserclass thus deals converting raw Markdown string into a Markdown Abstract Syntax Tree. -
In addition, we opted to not build a standalone generator component. Instead, we make it such that each node in our Markdown Abstract Syntax Tree supports a
toHtmlmethod, which returns the HTML representation of the tree starting from itself as a node. This way, we can leverage on OOP’s polymorphism to generate the HTML string out of our Markdown Abstract Syntax Tree a lot easier.
4. Documentation
Refer to the guide here.
5. Testing
Refer to the guide here.
6. Dev Ops
Refer to the guide here.
Appendix A: Product Scope
Target user profile:
-
Students that has a need to take notes and organize them into categories
-
prefer desktop apps over other types
-
can type fast
-
prefers typing over mouse input
-
is reasonably comfortable using CLI apps
Value proposition: Take and manage notes faster than a typical mouse/GUI driven app
Appendix B: User Stories
Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *
| Priority | As a … | I want to … | So that I can… |
|---|---|---|---|
|
student |
traverse my notes in a file system-like manner |
so that I can skim through my sea of notes and drafts without any problem. |
|
student |
search my notes by their content |
I won’t have to remember the exact location and title of notes. |
|
student |
reliably type search commands(not error-prone) |
focus on searching my notes rather than ensuring my commands are exact |
|
student |
can quickly make changes to a note |
so that I can update my notes with new information accurately while in class. |
|
impatient student |
alias a path to a folder |
do not have to memorise and type out the entire file structure when accessing a nested note |
Appendix C: Use Cases
(For all use cases below, the System is the Notably and the Actor is the user, unless specified otherwise)
Use case: Search notes using the Auto-suggestion feature
MSS
-
User types in a keyword of a note’s content that he wants to open.
-
Notably lists out the relevant search results, with the most relevant at the top of the list (based on the keyword’s number of occurrences in the note).
-
User chooses one of the suggested notes.
-
Notably opens the chosen note.
Use case ends.
Extensions
-
2a. No suggestion is being generated.
-
2a1. Notably displays a response text, indicating that the user is trying to search through all of the notes using that particular keyword.
-
2a2. Since the empty suggestion conveys that the keyword cannot be found, the user enters a new data.
-
Steps 2a1-2a2 are repeated until the data entered is correct. Use case resumes from Step 3.
Use case: Open/ Delete notes using the Auto-suggestion feature
MSS
-
User types in an incomplete path or title of a note.
-
Notably lists out suggestions of notes.
-
User chooses one of the suggested notes.
-
Notably opens/ deletes the chosen note.
Use case ends.
Extensions
-
1a. Path or title contains invalid character(s) ( symbols
-or`)-
1a1. Notably displays a response text, indicating that the path or title is invalid.
-
1a2. User enters a new data.
-
Steps 1a1-1a2 are repeated until the data entered is correct. Use case resumes from Step 2.
-
1b. Path or title does not exist
-
1b1. Notably displays a response text, indicating that the user is trying to open/ delete the note with the particular path or title that the user inputs.
-
1b2. Notably does not generate any suggestions, which means the note cannot be found.
-
1b3. User enters a new data.
-
Steps 1b1-1b3 are repeated until the data entered is correct. Use case resumes from Step 2.
Use case: Quickly Edit notes using the Edit Modal
MSS
-
User invokes edit command by typing "edit" in the command box.
-
Notably immediately opens an edit modal for the currently open note to allow for editing.
-
User presses a keyboard shortcut ("ESC").
-
Notably saves the user’s changes
-
Notably closes the edit modal.
Use case ends.
Extensions
-
2a. User does not make any edits to the content of the note.
Proceed to 3 first, then resume use case from 5.
-
2b. User edits the content of the note.
Use case continues from 3.
{More to be added}
Appendix D: Non Functional Requirements
-
Should work on any mainstream OS as long as it has Java
11or above installed. -
Should be able to hold up to 1000 notes without a noticeable sluggishness in performance for typical usage.
-
A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
Appendix F: Instructions for Manual Testing
Given below are instructions to test the app manually.
| These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing. |
F.1. Launch and Shutdown
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file
Expected: Shows the GUI with a set of sample Notes. The window size may not be optimum.
-
-
Saving window preferences
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-
F.2. Add a new Note
-
Adding a new Note to Notably without immediately opening that Note.
-
Prerequisites: None of the child(ren) Note(s) of the currently opened Note has the same title as the new Note.
Moreover, the currently opened Note must be the directory where the user wants to store the new Note. -
Command:
new -t Notably
Expected: a new Note titled "Notably" is added to the currently opened Note. The currently opened Note (current working directory) remains the same (highlighted at the sidebar).
-
-
Adding a new Note to Notably and immediately opening that Note
-
Prerequisites: None of the child(ren) Note(s) of the currently opened Note has the same title as the new Note.
Moreover, the currently opened Note must be the directory where the user wants to store the new Note. -
Command:
new -t Notably -o
Expected: a new Note titled "Notably" is added to the currently opened Note. The currently opened Note (current working directory) is now the newly created "Notably" note (highlighted at the sidebar).
-
F.3. Open a Note
-
Opening a Note in Notably.
-
Prerequisites: The Note that is about to be opened must not be the root Note.
-
Command:
open [-t] Notably
Expected: The Note titled "Notably" will be opened, with its content being displayed in the UI. The label "Notably" at the sidebar will also be highlighted, to indicate that the Note is being opened.
-
F.4. Delete a Note
-
Deleting a Note in Notably.
-
Prerequisites: The Note that is about to be deleted must not be the root Note.
-
Command:
delete [-t] Notably
Expected: The Note titled "Notably" will be deleted. The other nested Notes inside the Note "Notably" will also be deleted.
The Note "Notably" will no longer be visible in the sidebar.
-
F.5. Edit a Note
-
Editing a Note in Notably.
-
Prerequisites: The Note that is about to be edited must not be the root Note.
The currently opened Note must be the Note that the user wants to edit. -
Command:
edit
Expected: An edit modal will pop up displaying your Note’s content in HTML format. The user can edit and save the Note by exiting that modal.
-
F.6. Search for a Note based on a keyword
-
Searching for a note by using a keyword in Notably.
-
Prerequisites: -
-
Command:
search [-s] hello
Expected: A list of suggestions will be displayed, sorted based on the number of keyword matches, i.e. the note having the highest number of "hello" in its body will be put at the top of the suggestion list.
-
F.7. Correct command
-
Correct command word in user input.
-
Prerequisites: There should exist a Note titled Notably.
-
Command: 'delete [-t] Notably'
-
Expected: The command is interpreted as a delete command by Notably.
-
-
Correct path in user input.
-
Prerequisites: There should exist a Note titled Notably.
-
Command: 'delete [-t] Notbly'
-
Expected: The command is interpreted as a delete command by Notably.
-