Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New icon, Writing Stats, and README #177

Merged
merged 4 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
64 changes: 47 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<a href="https://github.com/midnightprioriem/calamus/releases"><img src="https://img.shields.io/github/release/midnightprioriem/calamus.svg" alt="github release version"></a>
<a href="https://github.com/midnightprioriem/calamus/releases"><img src="https://img.shields.io/github/downloads/midnightprioriem/calamus/total.svg" alt="github release downloads"></a>

<img width="1391" alt="app" src="https://user-images.githubusercontent.com/6120081/172929435-fa370411-1a5a-4bcf-ad36-3ffc54d09676.png">
<img width="1391" alt="app" src="assets/app_preview.png">


</div>
Expand All @@ -31,46 +31,76 @@ Calamus is a React Electron application for writing and publishing novels.

Calamus is currently still in pre-alpha stages---not all features are available. For a feature roadmap, please visit our <a href="https://github.com/midnightprioriem/calamus/projects/2" target="_blank">Project Board</a>.

- Open format project files (json)
- Markdown Editor
- LanguageTool Integration
- Customizable Interface
- Print-ready PDF export
- Custom print formatting

#### 🖊️ Write Your Next Novel

Calamus has what you need to organize and write your next novel. Organize your project into folders and use Markdown to make writing a breeze.

https://user-images.githubusercontent.com/6120081/172926916-38874b37-d46b-4995-8378-2fb09e563f38.mp4

#### 👐 Open Format

Calamus stores all of your project data in a human readable json format. In addition, all book content is written using Markdown, making all of your work truly portable. You can export all of your book's content into markdown files at any time.

https://user-images.githubusercontent.com/6120081/172928356-98c0f824-c98c-47cb-83b2-14ae3df1077b.mp4

##### .cala Format

<details>

Calamus project files use the `.cala` file extension, but are really just `json` files (yes this means you can edit `.cala` files by hand, but it is not recommended!). See below for a table detailing the properties inside of a `.cala` file.

| Property Name | Description |
|---------------|----------------------------------------------------------------------------------|
| bookTitle | The novel's title. |
| bookSubTitle | YThe novel's sub title. This is an optional property. |
| authorName | The novel's author name. |
| seriesName | The name of the series the novel is a part of. This is an optional property. |
| ISBN | The novel's ISBN number. |
| language | The language the novel is written in. |
| publisher | The name of the novel's publisher. |
| content | Array containing the novel's content. See a table detailing the `Section` below. |
| Property Name | Description |
|-------------------|----------------------------------------------------------------------------------|
| bookTitle | The novel's title. |
| bookSubTitle | YThe novel's sub title. This is an optional property. |
| authorName | The novel's author name. |
| seriesName | The name of the series the novel is a part of. This is an optional property. |
| ISBN | The novel's ISBN number. |
| language | The language the novel is written in. |
| publisher | The name of the novel's publisher. |
| content | Array containing the novel's content. See a table detailing the `Section` below. |
| publishSettings | `PublishSettings` object. See a table detailing `PublishSettings` below. |

The content property contains a JSON array of the `Section` object type, detailed below.

| Property Name | Description |
|-----------------|-------------------------------------------------------------------------------------------------------------|
| id | The name of the section. Appears as the name in the project sidebar. **Must be unique**. |
| id | A uniquely generated identifier. |
| name | The section's name. |
| content | Minified string of markdown content. Newlines are replaced with `\n` and `"` with `\"`. |
| type | Section type. One of 4 values: `folder`, `maincontent`, `frontmatter`, `backmatter`. |
| canHaveChildren | `true` or `false` value indicating whether the Section can have children. Only valid for `folder` sections. |
| children | A JSON array of `Section[]`. Only valid for `folder` sections. |
| collapsed | `true` or `false` value indicating whether then section is collapsed. Only valid for `folder` sections. |

| depth | Depth of the section in the tree. Starts at 0. |
| index | Index of section related to its children. Starts at 0. |
| parentId | For child sections, this is equal to the id of the section's parent |

The `PublishSettings` object type contains the book's formatting settings for eBook and print PDF.

| Property Name | Description |
|--------------------------------|------------------------------------------------------------------------------------------------|
| dropCap | Boolean value indicating whether or not to use a drop cap at the beginning of chapters. |
| dropCapEnableAdvancedSettings | Boolean value for enabling advanced drop cap settings. |
| dropCapFont | An advanced drop cap setting for setting the drop cap to a different font. |
| dropCapLineHeight | An advanced drop cap setting for adjusting the line height of the drop cap. |
| dropCapBottomMargin | An advanced drop cap setting for adjusting the bottom margin of the drop cap. |
| leadIn | Lead in (first line of each chapter) type. Can be 'None', 'Small Caps', or 'Italics' |
| sceneBreak | A string that will be used for scene breaks. Replaces horizontal rules. |
| rectoPageHeaders | Header for recto (odd) pages. Can be 'None', 'Chapter Title', 'Book Title', or 'Author Name' |
| versoPageHeaders | Header for verso (even) pages. Can be 'None', 'Chapter Title', 'Book Title', or 'Author Name' |
| paragraphFont | Font to use for paragraph text. Can be any font installed on system. |
| fontSize | Font size to use for paragraph text. |
| lineHeight | Paragraph line height. Can be 'Single', '1.5', or 'Double'. |
| dropFolio | Boolean value indicating whether or not to add a drop folio to the first page of each chapter. |
| topMargin | Top margin of each page in inches. |
| bottomMargin | Bottom margin of each page in inches. |
| insideMargin | Inside margin of each page in inches. |
| outsideMargin | Outside margin of each page in inches. |
| trimSize | Trim size of the book. i.e., '5in x 8in' |

</details>

Expand Down
16 changes: 16 additions & 0 deletions app/renderer/components/SidebarProjectContent.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import styled from 'styled-components';
import Color from 'color';
import { useBookWordCount } from 'renderer/hooks';
import { Button } from '../controls';
import useStore from '../store/useStore';
import SidebarProjectSections from './SidebarProjectSections';
Expand Down Expand Up @@ -73,11 +74,21 @@ const StyledAnchor = styled.a`
}
`;

const StyledWordCount = styled.div`
color: ${(p) => p.theme.sidebarFgTextSecondary};
font-size: 0.85em;
padding: 5px;
`;

const SidebarProjectContent = () => {
const isProjectOpen = useStore((state) => state.isProjectOpen);
const bookTitle = useStore((state) => state.bookTitle);
const authorName = useStore((state) => state.authorName);
const setNewBookModalOpen = useStore((state) => state.setNewBookModalOpen);
const showBookWordCount = useStore(
(state) => state.settings.writingStats.showWordCount
);
const bookWordCount = useBookWordCount();

return (
<StyledSidebarProjectContent>
Expand All @@ -89,6 +100,11 @@ const SidebarProjectContent = () => {
<SaveIndicator />
</StyledTitleContainer>
<StyledAuthorName>{authorName}</StyledAuthorName>
{showBookWordCount && (
<StyledWordCount>
{bookWordCount.toLocaleString('en-US')} words
</StyledWordCount>
)}
</StyledTitleBlock>
<StyledContentBlock>
<SidebarProjectSections />
Expand Down
23 changes: 23 additions & 0 deletions app/renderer/components/settings/EditorSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,29 @@ export const EditorSettings = () => {
const [useCustomEndpoint, setUseCustomEndpoint] = useState(false);
return (
<ScrollContainer cssMixin={scrollerCss}>
<SettingSubSection>
<SettingSectionHeading>Writing Stats</SettingSectionHeading>
<Setting>
<SettingLabel>
Book Word Count{' '}
<SettingTooltip>
Show the total word count of the book in the sidebar
</SettingTooltip>
</SettingLabel>
<ToggleSwitch
onChange={(value) => {
setSettings({
...settings,
writingStats: {
...settings.writingStats,
showWordCount: value,
},
});
}}
value={settings.writingStats.showWordCount}
/>
</Setting>
</SettingSubSection>
<SettingSubSection>
<SettingSectionHeading>Smart Typography</SettingSectionHeading>
<Setting>
Expand Down
1 change: 1 addition & 0 deletions app/renderer/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export { default as useProjectHotkeys } from './useProjectHotkeys';
export { default as useCommandKeyString } from './useCommandKeyString';
export { default as useOnWindowResize } from './useOnWindowResize';
export { useAppVersion } from './useAppVersion';
export { useBookWordCount } from './useBookWordCount';
26 changes: 26 additions & 0 deletions app/renderer/hooks/useBookWordCount.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useState, useEffect } from 'react';
import useStore from 'renderer/store/useStore';
import { Sections } from 'types/types';

export const useBookWordCount = () => {
const content = useStore((state) => state.content);
const [wordCount, setWordCount] = useState(0);

const calculateWordCount = (sections: Sections): number => {
let wordCount = 0;
sections.forEach((section) => {
if (section.type === 'folder') {
wordCount += calculateWordCount(section.children);
} else {
wordCount += section.content.split(/\s+/).length;
}
});
return wordCount;
};

useEffect(() => {
setWordCount(calculateWordCount(content));
}, [content]);

return wordCount;
};
6 changes: 6 additions & 0 deletions app/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ export type Settings = {
languageToolEndpointUrl: string;
languageToolUsername: string;
languageToolApiKey: string;
writingStats: {
showWordCount: boolean;
};
};

export const defaultSettings = {
Expand All @@ -145,6 +148,9 @@ export const defaultSettings = {
languageToolEndpoint: 'api.languagetool.org',
languageToolUsername: '',
languageToolApiKey: '',
writingStats: {
showWordCount: false,
},
} as Settings;

/* Component Styling */
Expand Down
Binary file removed assets/6_2_2022_app_preview.png
Binary file not shown.
Binary file added assets/app_preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/calamus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/icon.icns
Binary file not shown.
Binary file modified assets/icon.ico
Binary file not shown.
Binary file modified assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/icons/1024x1024.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/icons/128x128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/icons/16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/icons/24x24.png
Binary file not shown.
Binary file modified assets/icons/256x256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/icons/32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/icons/48x48.png
Binary file not shown.
Binary file modified assets/icons/512x512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/icons/64x64.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/icons/96x96.png
Binary file not shown.