Skip to content

Commit

Permalink
Prepend (#58)
Browse files Browse the repository at this point in the history
* Add prepend options, do not store note name separately

* Hide note path and format for daily since it is already known

* Allow properties to merge for append and prepend

* Add behavior warning

* Hide folder when using daily note
kepano authored Sep 21, 2024
1 parent 0fcd55a commit e73c4ea
Showing 12 changed files with 172 additions and 94 deletions.
44 changes: 30 additions & 14 deletions src/core/popup.ts
Original file line number Diff line number Diff line change
@@ -73,15 +73,26 @@ async function handleClip() {
const noteNameField = document.getElementById('note-name-field') as HTMLInputElement;
const pathField = document.getElementById('path-name-field') as HTMLInputElement;

if (!vaultDropdown || !noteContentField || !noteNameField || !pathField) {
if (!vaultDropdown || !noteContentField) {
showError('Some required fields are missing. Please try reloading the extension.');
return;
}

const selectedVault = currentTemplate.vault || vaultDropdown.value;
const noteContent = noteContentField.value;
const noteName = noteNameField.value;
const path = pathField.value;
const isDailyNote = currentTemplate.behavior === 'append-daily' || currentTemplate.behavior === 'prepend-daily';

let noteName = '';
let path = '';

if (!isDailyNote) {
if (!noteNameField || !pathField) {
showError('Note name or path field is missing. Please try reloading the extension.');
return;
}
noteName = noteNameField.value;
path = pathField.value;
}

const properties = Array.from(document.querySelectorAll('.metadata-property input')).map(input => ({
name: input.id,
@@ -90,15 +101,11 @@ async function handleClip() {
}));

let fileContent: string;
if (currentTemplate.behavior === 'create') {
const frontmatter = await generateFrontmatter(properties as Property[]);
fileContent = frontmatter + noteContent;
} else {
fileContent = noteContent;
}
const frontmatter = await generateFrontmatter(properties as Property[]);
fileContent = frontmatter + noteContent;

try {
await saveToObsidian(fileContent, noteName, path, selectedVault, currentTemplate.behavior, currentTemplate.specificNoteName, currentTemplate.dailyNoteFormat);
await saveToObsidian(fileContent, noteName, path, selectedVault, currentTemplate.behavior);
setTimeout(() => window.close(), 50);
} catch (error) {
console.error('Error in handleClip:', error);
@@ -406,10 +413,19 @@ document.addEventListener('DOMContentLoaded', async function() {
}

const pathField = document.getElementById('path-name-field') as HTMLInputElement;
if (pathField) {
let formattedPath = await replaceVariables(tabId, template.path, variables, currentUrl);
pathField.value = formattedPath;
pathField.setAttribute('data-template-value', template.path);
const pathContainer = document.querySelector('.vault-path-container') as HTMLElement;

if (pathField && pathContainer) {
const isDailyNote = template.behavior === 'append-daily' || template.behavior === 'prepend-daily';

if (isDailyNote) {
pathField.style.display = 'none';
} else {
pathContainer.style.display = 'flex';
let formattedPath = await replaceVariables(tabId, template.path, variables, currentUrl);
pathField.value = formattedPath;
pathField.setAttribute('data-template-value', template.path);
}
}

const noteContentField = document.getElementById('note-content-field') as HTMLTextAreaElement;
5 changes: 3 additions & 2 deletions src/icons/icons.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Trash2, AlignLeft, Binary, List, Calendar, Clock, Ellipsis, SquareCheckBig, GripVertical, Settings, X, ChevronRight, ChevronDown, CopyPlus } from 'lucide';
import { Trash2, AlignLeft, Binary, List, Calendar, Clock, Ellipsis, SquareCheckBig, GripVertical, Settings, X, ChevronRight, ChevronDown, CopyPlus, AlertTriangle } from 'lucide';
import { createIcons } from 'lucide';

export const icons = {
@@ -15,7 +15,8 @@ export const icons = {
Settings,
X,
ChevronRight,
ChevronDown
ChevronDown,
AlertTriangle
};

export function initializeIcons(root: HTMLElement | Document = document) {
5 changes: 5 additions & 0 deletions src/managers/template-manager.ts
Original file line number Diff line number Diff line change
@@ -45,6 +45,11 @@ export async function saveTemplateSettings(): Promise<string[]> {
const templateChunks: { [key: string]: string[] } = {};

for (const template of templates) {
if (!template.noteNameFormat || template.noteNameFormat.trim() === '') {
warnings.push(`Warning: Template "${template.name}" has an empty note name format. Using default "{{title}}".`);
template.noteNameFormat = '{{title}}';
}

const [chunks, warning] = await prepareTemplateForSave(template);
templateChunks[STORAGE_KEY_PREFIX + template.id] = chunks;
if (warning) {
76 changes: 47 additions & 29 deletions src/managers/template-ui.ts
Original file line number Diff line number Diff line change
@@ -133,21 +133,20 @@ export function showTemplateEditor(template: Template | null): void {
if (templateProperties) templateProperties.innerHTML = '';

const pathInput = document.getElementById('template-path-name') as HTMLInputElement;
if (pathInput) pathInput.value = editingTemplate.path;
if (pathInput) pathInput.value = editingTemplate.path || '';

const behaviorSelect = document.getElementById('template-behavior') as HTMLSelectElement;

if (behaviorSelect) behaviorSelect.value = editingTemplate.behavior || 'create';
const specificNoteName = document.getElementById('specific-note-name') as HTMLInputElement;
if (specificNoteName) specificNoteName.value = editingTemplate.specificNoteName || '';
const dailyNoteFormat = document.getElementById('daily-note-format') as HTMLInputElement;
if (dailyNoteFormat) dailyNoteFormat.value = editingTemplate.dailyNoteFormat || 'YYYY-MM-DD';

const noteNameFormat = document.getElementById('note-name-format') as HTMLInputElement;
if (noteNameFormat) noteNameFormat.value = editingTemplate.noteNameFormat || '{{title}}';
if (noteNameFormat) {
noteNameFormat.value = editingTemplate.noteNameFormat || '{{title}}';
}

const noteContentFormat = document.getElementById('note-content-format') as HTMLTextAreaElement;
if (noteContentFormat) noteContentFormat.value = editingTemplate.noteContentFormat || '';

// Call updateBehaviorFields here to set the initial state
updateBehaviorFields();

if (behaviorSelect) {
@@ -222,24 +221,38 @@ export function showTemplateEditor(template: Template | null): void {

function updateBehaviorFields(): void {
const behaviorSelect = document.getElementById('template-behavior') as HTMLSelectElement;
const specificNoteContainer = document.getElementById('specific-note-container');
const dailyNoteFormatContainer = document.getElementById('daily-note-format-container');
const noteNameFormatContainer = document.getElementById('note-name-format-container');
const propertiesContainer = document.getElementById('properties-container');
const propertiesWarning = document.getElementById('properties-warning');
const pathContainer = document.getElementById('path-name-container');
const noteNameFormat = document.getElementById('note-name-format') as HTMLInputElement;
const behaviorWarningContainer = document.getElementById('behavior-warning-container');

if (behaviorSelect) {
const selectedBehavior = behaviorSelect.value;
if (specificNoteContainer) specificNoteContainer.style.display = selectedBehavior === 'append-specific' ? 'block' : 'none';
if (dailyNoteFormatContainer) dailyNoteFormatContainer.style.display = selectedBehavior === 'append-daily' ? 'block' : 'none';
if (noteNameFormatContainer) noteNameFormatContainer.style.display = selectedBehavior === 'create' ? 'block' : 'none';

if (selectedBehavior === 'append-specific' || selectedBehavior === 'append-daily') {
if (propertiesContainer) propertiesContainer.style.display = 'none';
if (propertiesWarning) propertiesWarning.style.display = 'block';
const isDailyNote = selectedBehavior === 'append-daily' || selectedBehavior === 'prepend-daily';

if (selectedBehavior !== 'create') {
if (behaviorWarningContainer) behaviorWarningContainer.style.display = 'flex';
} else {
if (propertiesContainer) propertiesContainer.style.display = 'block';
if (propertiesWarning) propertiesWarning.style.display = 'none';
if (behaviorWarningContainer) behaviorWarningContainer.style.display = 'none';
}

if (noteNameFormatContainer) noteNameFormatContainer.style.display = isDailyNote ? 'none' : 'block';
if (pathContainer) pathContainer.style.display = isDailyNote ? 'none' : 'block';

if (noteNameFormat) {
noteNameFormat.required = !isDailyNote;
switch (selectedBehavior) {
case 'append-specific':
case 'prepend-specific':
noteNameFormat.placeholder = 'Specific note name';
break;
case 'append-daily':
case 'prepend-daily':
noteNameFormat.placeholder = 'Daily note format (e.g., YYYY-MM-DD)';
break;
default:
noteNameFormat.placeholder = 'Note name format';
}
}
}
}
@@ -370,19 +383,24 @@ export function updateTemplateFromForm(): void {
}

const behaviorSelect = document.getElementById('template-behavior') as HTMLSelectElement;
if (behaviorSelect) template.behavior = behaviorSelect.value;
if (behaviorSelect) template.behavior = behaviorSelect.value as Template['behavior'];

const isDailyNote = template.behavior === 'append-daily' || template.behavior === 'prepend-daily';

const pathInput = document.getElementById('template-path-name') as HTMLInputElement;
if (pathInput) template.path = pathInput.value;

const noteNameFormat = document.getElementById('note-name-format') as HTMLInputElement;
if (noteNameFormat) template.noteNameFormat = noteNameFormat.value;

const specificNoteName = document.getElementById('specific-note-name') as HTMLInputElement;
if (specificNoteName) template.specificNoteName = specificNoteName.value;

const dailyNoteFormat = document.getElementById('daily-note-format') as HTMLInputElement;
if (dailyNoteFormat) template.dailyNoteFormat = dailyNoteFormat.value;
if (noteNameFormat) {
if (!isDailyNote && noteNameFormat.value.trim() === '') {
console.error('Note name format is required for non-daily note behaviors');
noteNameFormat.setCustomValidity('Note name format is required for non-daily note behaviors');
noteNameFormat.reportValidity();
return;
} else {
noteNameFormat.setCustomValidity('');
template.noteNameFormat = noteNameFormat.value;
}
}

const noteContentFormat = document.getElementById('note-content-format') as HTMLTextAreaElement;
if (noteContentFormat) template.noteContentFormat = noteContentFormat.value;
2 changes: 1 addition & 1 deletion src/popup.html
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@
<div id="vault-container" style="display: none;">
<select id="vault-select"></select>
</div>
<input id="path-name-field" type="text"/>
<input id="path-name-field" type="text" placeholder="Folder"/>
</div>
<button id="clip-button" class="mod-cta">Add to Obsidian</button>
</div>
32 changes: 14 additions & 18 deletions src/settings.html
Original file line number Diff line number Diff line change
@@ -145,26 +145,26 @@ <h2 id="template-editor-title">Edit template</h2>
<label for="template-behavior">Behavior</label>
<select id="template-behavior">
<option value="create">Create new note</option>
<option value="append-specific">Add to existing note</option>
<option value="append-daily">Add to daily note</option>
<option value="append-specific">Add to an existing note, at the bottom</option>
<option value="prepend-specific">Add to an existing note, at the top</option>
<option value="append-daily">Add to daily note, at the bottom</option>
<option value="prepend-daily">Add to daily note, at the top</option>
</select>
</div>
<div id="behavior-warning-container" class="warning-container" style="display: none;">
<div class="warning-icon">
<i data-lucide="alert-triangle"></i>
</div>
<div class="warning-text">
Adding to an existing note or daily note requires Obsidian 1.7.2 or later.
</div>
</div>
<div class="setting-item" id="note-name-format-container" style="display: none;">
<label for="note-name-format">Note name</label>
<div class="setting-item-description">Format for the file name when creating a new note. You can use variables like {{title}}, {{date}}, and {{published}} to pre-populate data from the&nbsp;page. <a href="https://github.com/obsidianmd/obsidian-clipper?tab=readme-ov-file#template-variables" target="_blank">Learn&nbsp;more</a>.</div>
<div class="setting-item-description">Format for the file name of the note. You can use variables like {{title}}, {{date}}, and {{published}} to pre-populate data from the&nbsp;page. <a href="https://github.com/obsidianmd/obsidian-clipper?tab=readme-ov-file#template-variables" target="_blank">Learn&nbsp;more</a>.</div>
<input type="text" id="note-name-format" placeholder="{{title}}" />
</div>
<div class="setting-item" id="specific-note-container" style="display: none;">
<label for="specific-note-name">Note to add to</label>
<div class="setting-item-description">The name of the note to add&nbsp;to.</div>
<input type="text" id="specific-note-name" placeholder="Note name" />
</div>
<div class="setting-item" id="daily-note-format-container" style="display: none;">
<label for="daily-note-format">Daily note format</label>
<div class="setting-item-description">Make sure this is the same format as your daily note template in Obsidian&nbsp;settings.</div>
<input type="text" id="daily-note-format" placeholder="YYYY-MM-DD" value="YYYY-MM-DD" />
</div>
<div class="setting-item">
<div class="setting-item" id="path-name-container" style="display: none;">
<label for="template-path-name">Note location</label>
<div class="setting-item-description">The folder or path of the note.</div>
<input type="text" id="template-path-name" placeholder="Clippings" />
@@ -181,10 +181,6 @@ <h2 id="template-editor-title">Edit template</h2>
<div class="setting-item-description">Define rules to automatically select this template if it matches a URL pattern or contains schema.org data. One rule per&nbsp;line.</div>
<textarea id="url-patterns" placeholder="https://example.com/" rows="2"></textarea>
</div>
<div id="properties-warning" style="display: none;">
<label>Properties</label>
<div class="setting-item-description">Properties are not available when adding to an existing&nbsp;note.</div>
</div>
<div id="properties-container">
<label>Properties</label>
<div class="setting-item-description">Properties to add to the top of the clipped note. Use variables to pre-populate data from the&nbsp;page.</div>
5 changes: 5 additions & 0 deletions src/styles/_variables.scss
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
--input-height: 1.875rem;

--radius-s: 4px;
--radius-m: 8px;

--text-normal: var(--color-base-100);
--text-muted: var(--color-base-70);
@@ -87,6 +88,8 @@
--color-base-70: #5c5c5c;
--color-base-100: #222222;

--color-orange-rgb: 236, 117, 0;

--mono-rgb-0: 255, 255, 255;
--mono-rgb-100: 0, 0, 0;

@@ -154,6 +157,8 @@
--color-base-60: #999999;
--color-base-70: #b3b3b3;
--color-base-100: #dadada;

--color-orange-rgb: 233, 151, 63;

--interactive-accent: var(--color-accent);
--interactive-accent-hover: var(--color-accent-1);
3 changes: 3 additions & 0 deletions src/styles/popup.scss
Original file line number Diff line number Diff line change
@@ -229,6 +229,9 @@
display: flex;
flex-direction: row;
gap: 8px;
#vault-container {
flex-grow: 1;
}
#vault-select {
font-weight: 500;
min-width: 100px;
18 changes: 18 additions & 0 deletions src/styles/settings.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
.logo {
fill: var(--color-accent-1);
}
.warning-container {
background-color: rgba(var(--color-orange-rgb), 0.1);
padding: 12px;
border-radius: var(--radius-m);
margin: 8px 0 0;
font-size: var(--font-ui-smaller);
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
.warning-icon {
display: flex;
align-items: center;
justify-content: center;
color: rgb(var(--color-orange-rgb));
}
}

#settings {
margin: 0 auto;
padding: 0;
4 changes: 1 addition & 3 deletions src/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
export interface Template {
id: string;
name: string;
behavior: string;
behavior: 'create' | 'append-specific' | 'append-daily' | 'prepend-specific' | 'prepend-daily';
noteNameFormat: string;
path: string;
noteContentFormat: string;
properties: Property[];
triggers?: string[];
specificNoteName?: string;
dailyNoteFormat?: string;
vault?: string;
}

Loading

0 comments on commit e73c4ea

Please sign in to comment.