diff --git a/.eslintrc.json b/.eslintrc.json
index 0e3ec36..92699ab 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -26,6 +26,7 @@
"HTMLParagraphElement",
"HTMLSpanElement",
"Promise",
+ "Range",
"URL",
"Window"
]
diff --git a/src/css/editor.css b/src/css/editor.css
index 04aa21b..0f834a7 100644
--- a/src/css/editor.css
+++ b/src/css/editor.css
@@ -18,6 +18,7 @@ button, input, select, textarea {
}
pre, code, samp, kbd, var {
font-family: var(--monospace-font);
+ font-variant-ligatures: none;
}
.container {
@@ -26,14 +27,17 @@ pre, code, samp, kbd, var {
height: 100vh;
--accent: #0080b3;
- --background-color: #f2f2f2;
- --text-color: #000;
- --text-secondary-color: #666;
- --link-color: var(--accent);
+ --background-color: #181818;
+ --text-color: #CCCCCC;
+ --text-secondary-color: #9B9B9B;
+ --border-color: #2B2B2B;
+
+ color: var(--text-color);
+ background-color: var(--background-color);
}
.container .separator {
- background: #ddd;
+ background-color: var(--border-color);
display: flex;
align-items: center;
justify-content: center;
@@ -55,7 +59,7 @@ pre, code, samp, kbd, var {
.container .separator:hover,
body.dragging .container .separator {
background-color: var(--accent);
- color: #fff;
+ color: var(--text-color);
}
body.dragging,
body.dragging .container .separator {
@@ -67,14 +71,14 @@ body.dragging .container .separator {
.container .editor-footer {
font-size: 0.8rem;
padding: .25rem .5rem;
- background-color: #ddd;
+ border-top: 1px solid var(--border-color);
}
.container .editor-header {
display: flex;
padding: .5rem;
- background: #ddd;
align-items: center;
+ border-bottom: 1px solid var(--border-color);
}
.container .editor-header .status {
padding-right: .5rem;
@@ -102,7 +106,7 @@ body.dragging .container .separator {
padding: .5rem 1rem;
border: 0;
background: var(--accent);
- color: #fff;
+ color: var(--text-color);
border-radius: .25rem;
margin-left: auto;
}
@@ -123,6 +127,14 @@ body.dragging .container .separator {
height: 100%;
display: flex;
flex-direction: column;
+
+ --syntax-background: #1F1F1F;
+ --syntax-color: #CCCCCC;
+ --syntax-color-1: #569cd6;
+ --syntax-color-2: #CE9178;
+ --syntax-color-3: #6A9956;
+ --syntax-color-4: #9CDCFE;
+ --syntax-line-number-color: #6D7681;
}
.container .editor-container .editor-content {
overflow: auto;
@@ -130,10 +142,9 @@ body.dragging .container .separator {
height: 100%;
display: flex;
flex-direction: column;
-}
-.container .editor-container .editor-content #savedContent {
- display: none;
+ color: var(--syntax-color);
+ background-color: var(--syntax-background);
}
.container .editor-container pre {
@@ -142,6 +153,8 @@ body.dragging .container .separator {
padding: 1rem 1rem 50% 1rem;
font-size: 0.8rem;
}
+.container .editor-container pre > div {
+}
.container .editor-container pre.contentPre {
position: absolute;
top: 0;
@@ -149,19 +162,22 @@ body.dragging .container .separator {
right: 0;
bottom: 0;
color: transparent;
- /* color: rgba(0,0,0,.3); */
- caret-color: var(--text-color);
+}
+.container .editor-container pre.contentLinesPre {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
}
.container .editor-container pre.contentStylePre {
flex-grow: 1;
- color: var(--text-color);
- background-color: var(--background-color);
-
- --token-color: #999;
-
+ color: var(--syntax-color);
+ /* background-color: var(--syntax-background); */
}
.container .editor-container pre.contentPre #content {
outline: 0;
+ caret-color: var(--syntax-color);
/**
* Prevent contenteditable to add p, div items on ENTER
@@ -208,65 +224,122 @@ body.dragging .container .preview-container .drag-overlay {
.mdFrontMatter {
color: var(--text-secondary-color);
- background: rgba(0,0,0,0.025);
+ /* background: rgba(255,255,255,.025);
display: block;
padding: 1rem 1rem .5rem 1rem;
margin: -1rem -1rem -.5rem -1rem;
- border-radius: 4px;
-}
-.mdEm {
- font-style: italic;
-}
-.mdStrong {
- font-weight: bold;
-}
-.mdLink {
- color: var(--link-color);
-}
-.mdHeading {
- font-weight: bold;
+ border-radius: 4px; */
}
+
+/* visible characters */
+
.mdAsterisk::after {
content: "*";
- color: var(--token-color);
}
.mdUnderscore::after {
content: "_";
- color: var(--token-color);
}
.mdHashmark::after {
content: "#";
- color: var(--token-color);
-}
-.mdHtml {
- color: #fe7b00;
-}
-.mdQuote {
- color: darkmagenta;
}
.mdGreaterThan::after {
content: ">";
- color: var(--token-color);
}
.mdDash::after {
content: "-";
- color: var(--token-color);
-}
-.mdListBullet {
- color: var(--token-color);
-}
-.mdList {
- color: darkgreen;
}
.mdEqual::after {
content: "=";
- color: var(--token-color);
}
+.mdTilde::after {
+ content: "~";
+}
+.mdBacktick::after {
+ content: "`";
+}
+
+/* invisible characters */
.mdNewLine::after {
content: "\21B5";
color: var(--token-color);
opacity: .3;
}
+
+/* elements */
+
+.mdHeading {
+ font-weight: bold;
+ color: var(--syntax-color-1);
+}
+.mdStrong {
+ font-weight: bold;
+ color: var(--syntax-color-1);
+}
+.mdEm {
+ font-style: italic;
+}
+.mdStrikethrough {
+ text-decoration: line-through;
+}
+.mdLink {
+
+}
+.mdLink .mdLinkText {
+ color: var(--syntax-color-2);
+}
+.mdLink .mdLinkUrl {
+ text-decoration: underline;
+}
+.mdLink .mdLinkTitle {
+ color: var(--syntax-color-2);
+}
+.mdHtml {
+ color: var(--syntax-color-1);
+}
+.mdHtml .mdHtmlAttrName {
+ color: var(--syntax-color-4);
+}
+.mdHtml .mdHtmlAttrValue {
+ color: var(--syntax-color-2);
+}
+.mdCode {
+ color: var(--syntax-color-2);
+}
+.mdCodeBlock {
+ color: var(--syntax-color-2);
+}
+.mdQuote {
+
+}
+.mdQuote .mdGreaterThan {
+ color: var(--syntax-color-3);
+}
+.mdList {
+
+}
+.mdList .mdListBullet {
+ color: var(--syntax-color-1);
+}
.mdHr {
-}
\ No newline at end of file
+}
+
+
+.mdLine {
+ display: inline-flex;
+}
+.mdLine .mdLineNumber {
+ text-align: right;
+ flex-shrink: 0;
+ color: var(--syntax-line-number-color);
+}
+.mdLine.mdLineActive .mdLineNumber {
+ color: var(--syntax-color);
+}
+.mdLine .mdLineContent {
+ flex-grow: 1;
+ padding-left: 1ch;
+ color: transparent;
+}
+
+/* --syntax-color-1 */
diff --git a/src/ejs/editor.ejs b/src/ejs/editor.ejs
index 71c3cd4..14d5554 100644
--- a/src/ejs/editor.ejs
+++ b/src/ejs/editor.ejs
@@ -35,8 +35,8 @@
Save
-
<%= context.rawContent %>
-
<%= context.rawContent %>
+
+
<%= context.rawContent %>
diff --git a/src/js/client/editor.js b/src/js/client/editor.js
index 895e019..746fc5d 100644
--- a/src/js/client/editor.js
+++ b/src/js/client/editor.js
@@ -3,6 +3,25 @@ import { calculateReadingTime, countWords } from "../server/shared";
import "../../css/editor.css";
+const contentScrollHandler = () => {
+ const editorScrollValue = document.querySelector(".editor-content").scrollTop;
+ const editorContentHeight = document.querySelector(".editor-content pre").offsetHeight;
+ const editorContainerHeight = document.querySelector(".editor-content").offsetHeight;
+
+ const readPercentage = getReadPercentage(editorScrollValue, editorContentHeight, editorContainerHeight);
+
+ document.querySelectorAll("iframe").forEach((iframe) => {
+ /** @type {Window} */
+ const iframeWindow = iframe.contentWindow || iframe;
+
+ const iframeContainerHeight = iframeWindow.innerHeight;
+ const iframeContentHeight = iframeWindow.document.querySelector("body").clientHeight;
+ const iframeScrollValue = getScrollValue(readPercentage, iframeContentHeight, iframeContainerHeight);
+
+ iframeWindow.scrollTo(0, iframeScrollValue);
+ });
+};
+
let savedContent = null;
let isLoading = 0;
const updateStatus = () => {
@@ -20,24 +39,85 @@ const updateStatus = () => {
document.querySelector(".container").className = document.querySelector(".container").className.replaceAll(/( status-loading| status-saved| status-changed)/gm, "");
document.querySelector(".container").className += ` status-${status}`;
+ if (status === "loading" || status === "saved") {
+ document.querySelector("form.editor-container button[name=save]").setAttribute("disabled", true);
+ } else {
+ document.querySelector("form.editor-container button[name=save]").removeAttribute("disabled");
+ }
+
return status;
};
+/**
+ *
+ * @param {HTMLElement} editableContent
+ * @returns {Range}
+ */
+const getRange = (editableContent) => {
+ if (window.getSelection) {
+ const sel = window.getSelection();
+ if (sel.rangeCount) {
+ const range = sel.getRangeAt(0);
+ if (range.commonAncestorContainer.parentNode == editableContent) {
+ return range;
+ }
+ }
+ }
+ return null;
+}
+
let iframeId = (new Date()).getTime();
let changeThrottle = null;
const changeHandler = (event) => {
- let markdownText = document.getElementById("content").innerText;
- if (!savedContent) savedContent = markdownText;
-
- const status = updateStatus();
+ /* fix Grammarly blend mode issue with dark content */
+ // document.querySelectorAll("grammarly-extension").forEach((gE) => {
+ // const styleNode = document.createElement("style");
+ // styleNode.textContent = `
+ // div[data-grammarly-part="highlight"] {
+ // mix-blend-mode: normal !important;
+ // }
+ // `;
+ // gE.shadowRoot.appendChild(styleNode);
+ // });
+
+
+ const editableContent = document.getElementById("content");
+ const styleContent = document.getElementById("contentStyle");
+ const linesContent = document.getElementById("contentLines");
+
+ const editorInfo = {
+ line: 0,
+ column: 0,
+ wordCount: 0,
+ readingTime: 0,
+ };
- if (status === "loading" || status === "saved") {
- document.querySelector("form.editor-container button[name=save]").setAttribute("disabled", true);
- } else {
- document.querySelector("form.editor-container button[name=save]").removeAttribute("disabled");
+ let markdownText = editableContent.innerText;
+
+ const selection = getRange(editableContent);
+ if (selection) {
+ let charCount = 0;
+ markdownText.split("\n").forEach((l, idx) => {
+ const line = `${l}\n`;
+ const lineStart = charCount;
+ const lineEnd = charCount + line.length;
+ if (selection.startOffset >= lineStart && selection.startOffset < lineEnd) {
+ // console.log("start:", idx, selection.startOffset - lineStart, line);
+ }
+ if (selection.endOffset >= lineStart && selection.endOffset < lineEnd) {
+ // console.log("end:", idx, selection.endOffset - lineStart, line);
+ editorInfo.line = idx + 1;
+ editorInfo.column = selection.endOffset - lineStart + 1;
+ }
+ charCount = lineEnd;
+ });
}
+ if (!savedContent) savedContent = markdownText;
+
+ updateStatus();
+
const markupMap = {
"*": " ",
"_": " ",
@@ -45,29 +125,44 @@ const changeHandler = (event) => {
">": " ",
"-": " ",
"=": " ",
+ "~": " ",
+ "`": " ",
"newline": " ",
"heading": "%% ",
"em": "%% ",
"strong": "%% ",
+ "strikethrough": "%% ",
"quote": "%% ",
"list": "%% ",
"list_bullet": "%% ",
- "link": "%% ",
+ "link": "[%1 ](%2 %3 ) ",
+ "code": "%% ",
+ "code_block": "%% ",
"html": "%% ",
"hr": "%% ",
};
- let frontMatter = '';
markdownText = markdownText.replaceAll("<", "<").replaceAll(">", ">");
+
+ const gutterSize = `${markdownText.split("\n").length}`.length;
+ linesContent.innerHTML = markdownText.split("\n").map((line, idx) => {
+ return `` +
+ `${idx + 1} ` +
+ `${line} ` +
+ " ";
+ }).slice(0, -1).join("\n") + "\n";
+ styleContent.style.paddingLeft = `${gutterSize + 1}ch`;
+ editableContent.style.paddingLeft = `${gutterSize + 1}ch`;
+
+ let frontMatter = '';
const mdFrontMatterMatch = markdownText.match(/---.*?---\n/ms);
if (mdFrontMatterMatch) {
frontMatter = `${mdFrontMatterMatch[0]} `;
markdownText = markdownText.replace(mdFrontMatterMatch[0], '');
}
- const wordCount = countWords(markdownText);
- const readingTime = calculateReadingTime(markdownText);
- document.querySelector(".editor-footer").innerHTML = `${wordCount} words | ${readingTime} minute${readingTime > 1 ? "s" : ""}`;
+ editorInfo.wordCount = countWords(markdownText);
+ editorInfo.readingTime = calculateReadingTime(markdownText);
const mdHeadingMatch = markdownText.matchAll(/^(#{1,6})(\s+.*)/gm);
if (mdHeadingMatch) [...mdHeadingMatch].forEach(match => {
@@ -87,9 +182,27 @@ const changeHandler = (event) => {
markdownText = markdownText.replace(match[0], markupMap.hr.replace("%%", `${markup}`));
});
- const mdLinkMatch = markdownText.matchAll(/!?\[[^\]]+?\]\([^\s]+?(\s+".*?")?\)/gm);
+ const mdCodeBlockMatch = markdownText.matchAll(/(```)(\s*[^\s]?)(.*?)\1\n/gms);
+ if (mdCodeBlockMatch) [...mdCodeBlockMatch].forEach(match => {
+ const markup = match[1].split("").map((m) => markupMap[m]).join("");
+ console.log(match);
+ markdownText = markdownText.replaceAll(match[0], markupMap.code_block.replace("%%", `${markup}${match[2] || ''}${match[3]}${markup}`));
+ });
+
+ const mdCodeMatch = markdownText.matchAll(/(`)(.+?)\1/gms);
+ if (mdCodeMatch) [...mdCodeMatch].forEach(match => {
+ if (match[2].match(/\n\n/)) return;
+ const markup = match[1].split("").map(m => markupMap[m]).join("");
+ markdownText = markdownText.replace(match[0], markupMap.code.replace("%%", `${markup}${match[2]}${markup}`));
+ });
+
+ const mdLinkMatch = markdownText.matchAll(/!?\[([^\]]+?)\]\(([^\s]+?)(\s+".*?")?\)/gm);
if (mdLinkMatch) [...mdLinkMatch].forEach(match => {
- markdownText = markdownText.replaceAll(match[0], markupMap.link.replace("%%", match[0]));
+ markdownText = markdownText.replaceAll(match[0], markupMap.link
+ .replace("%1", match[1])
+ .replace("%2", match[2])
+ .replace("%3", match[3] || '')
+ );
});
const mdQuoteMatch = markdownText.matchAll(/^([ \t]*)(>(\s*>)*)(.*?\n\n)/gms);
@@ -110,13 +223,20 @@ const changeHandler = (event) => {
if (mdListMatch) [...mdListMatch].forEach(match => {
let content = match[3];
if (content.match(/^([ \t]*)(\*|\+|-|\d+\.)(\s+.*?\n\n)/ms)) content = listMatch(content);
- const markup = markupMap[match[2]] ? markupMap[match[2]] : markupMap.list_bullet.replace("%%", match[2]);
+ const markup = markupMap.list_bullet.replace("%%", markupMap[match[2]] ? markupMap[match[2]] : match[2]);
markdownText = markdownText.replace(match[0], `${match[1]}${markupMap.list.replace("%%", `${markup}${content}`)}`);
});
return markdownText;
}
markdownText = listMatch(markdownText);
+ const mdStrikethroughMatch = markdownText.matchAll(/(~~)(.+?)\1/gms);
+ if (mdStrikethroughMatch) [...mdStrikethroughMatch].forEach(match => {
+ if (match[2].match(/\n\n/)) return;
+ const markup = match[1].split("").map(m => markupMap[m]).join("");
+ markdownText = markdownText.replace(match[0], markupMap.strikethrough.replace("%%", `${markup}${match[2]}${markup}`));
+ });
+
const mdStrongMatch = markdownText.matchAll(/(\*\*|__)(.+?)\1/gms);
if (mdStrongMatch) [...mdStrongMatch].forEach(match => {
if (match[2].match(/\n\n/)) return;
@@ -133,12 +253,22 @@ const changeHandler = (event) => {
const mdHtmlMatch = markdownText.matchAll(/<[\w/].*?>/gm);
if (mdHtmlMatch) [...mdHtmlMatch].forEach(match => {
- markdownText = markdownText.replace(match[0], markupMap.html.replace("%%", match[0]));
+ let content = match[0];
+ const attributeMatch = match[0].matchAll(/([^\s]+=)((['"]).*?\3)/gm);
+ if (attributeMatch) [...attributeMatch].forEach((attrMatch) => {
+ content = content.replaceAll(attrMatch[0], `${attrMatch[1]} ${attrMatch[2]} `);
+ });
+ markdownText = markdownText.replace(match[0], markupMap.html.replace("%%", content));
});
markdownText = markdownText.replaceAll(/\n/gms, `${markupMap.newline}\n`);
document.getElementById("contentStyle").innerHTML = frontMatter + markdownText;
+ document.querySelector(".editor-footer").innerHTML = `
+ Ln ${editorInfo.line}, Col ${editorInfo.column} |
+ ${editorInfo.wordCount} words |
+ ${editorInfo.readingTime} minute${editorInfo.readingTime > 1 ? "s" : ""}
+ `;
window.clearTimeout(changeThrottle);
changeThrottle = window.setTimeout(() => {
@@ -152,18 +282,20 @@ const changeHandler = (event) => {
const newIframe = document.createElement("iframe");
newIframe.setAttribute("name", `temp_iframe_${iframeId}`);
document.querySelector(".preview-container").appendChild(newIframe);
- newIframe.addEventListener("load", (event) => {
- event.target.style.opacity = 1;
+ window.iframeLoaded = () => {
+ const iframeName = document.querySelector("form.editor-container").getAttribute('target');
+ document.querySelector(`iframe[name="${iframeName}"]`).style.opacity = 1;
document.querySelectorAll("iframe").forEach(iframe => {
- if (iframe.getAttribute("name") < event.target.getAttribute("name")) {
+ if (iframe.getAttribute("name") < iframeName) {
iframe.remove();
}
});
-
+
isLoading--;
updateStatus();
+ contentScrollHandler();
+ };
- });
newIframe.addEventListener("error", (event) => {
console.log(error);
});
@@ -181,31 +313,16 @@ changeHandler();
const submitHandler = (event) => {
if (event.submitter.name === "save") {
savedContent = event.target.content.value;
- changeHandler();
+ // changeHandler();
}
};
-const contentScrollHandler = () => {
- const editorScrollValue = document.querySelector(".editor-content").scrollTop;
- const editorContentHeight = document.querySelector(".editor-content pre").offsetHeight;
- const editorContainerHeight = document.querySelector(".editor-content").offsetHeight;
-
- const readPercentage = getReadPercentage(editorScrollValue, editorContentHeight, editorContainerHeight);
-
- document.querySelectorAll("iframe").forEach((iframe) => {
- /** @type {Window} */
- const iframeWindow = iframe.contentWindow || iframe;
-
- const iframeContainerHeight = iframeWindow.innerHeight;
- const iframeContentHeight = iframeWindow.document.querySelector("body").clientHeight;
- const iframeScrollValue = getScrollValue(readPercentage, iframeContentHeight, iframeContainerHeight);
-
- iframeWindow.scrollTo(0, iframeScrollValue);
- });
+const saveHandéer = () => {
+ let markdownText = document.getElementById("content").innerText;
+ document.querySelector("form.editor-container input[name=content]").value = markdownText;
+ savedContent = markdownText;
};
-window.contentScrollHandler = contentScrollHandler;
-
let separatorDragging = false;
const separatorDragStartHandler = (event) => {
@@ -228,17 +345,21 @@ const separatorDragStopHandler = (event) => {
}
};
-"keyup paste input".split(" ").forEach(eventType => document.getElementById("content").addEventListener(eventType, changeHandler));
+"focus keyup paste input click".split(" ").forEach(eventType => document.getElementById("content").addEventListener(eventType, changeHandler));
document.querySelector("form.editor-container").addEventListener("submit", submitHandler);
+// document.querySelector("button[name=save]").addEventListener("click", saveHandéer);
document.querySelector(".editor-content").addEventListener("scroll", contentScrollHandler);
document.querySelector(".separator").addEventListener("mousedown", separatorDragStartHandler);
window.addEventListener("mousemove", separatorDragHandler);
window.addEventListener("mouseup", separatorDragStopHandler);
+document.getElementById("content").focus();
+
if (import.meta.webpackHot) {
import.meta.webpackHot.dispose(() => {
- "keyup paste input".split(" ").forEach(eventType => document.getElementById("content").removeEventListener(eventType, changeHandler));
+ "focus keyup paste input click".split(" ").forEach(eventType => document.getElementById("content").removeEventListener(eventType, changeHandler));
document.querySelector("form.editor-container").removeEventListener("submit", submitHandler);
+ // document.querySelector("button[name=save]").removeEventListener("click", saveHandéer);
document.querySelector(".editor-content").removeEventListener("scroll", contentScrollHandler);
document.querySelector(".separator").removeEventListener("mousedown", separatorDragStartHandler);
window.removeEventListener("mousemove", separatorDragHandler);
diff --git a/src/js/client/preview.js b/src/js/client/preview.js
index 6ba29fe..fb45da7 100644
--- a/src/js/client/preview.js
+++ b/src/js/client/preview.js
@@ -1,30 +1,6 @@
-// import { getReadPercentage, getScrollValue } from "./common";
-
-// window.scrollTo(0, window.parent.iframeScrollY);
-window.parent.contentScrollHandler();
-
-// const scrollHandler = async (event) => {
-// const scrollValue = window.scrollY;
-// const contentHeight = document.querySelector('body').clientHeight;
-// const containerHeight = window.innerHeight;
-
-// const readPercentage = getReadPercentage(scrollValue, contentHeight, containerHeight);
-
-// const parentContainerHeight = window.parent.document.querySelector(".editor-content").offsetHeight;
-// const parentContentHeight = window.parent.document.querySelector(".editor-content pre").offsetHeight;
-// const parentScrollValue = getScrollValue(readPercentage, parentContentHeight, parentContainerHeight);
-
-// console.log(parentScrollValue);
-
-// window.parent.document.querySelector(".editor-content").scrollTop = parentScrollValue;
-// };
-
-// window.addEventListener("scroll", scrollHandler);
+window.parent.iframeLoaded();
if (import.meta.webpackHot) {
- import.meta.webpackHot.dispose(() => {
- // scrollHandler();
- // window.removeEventListener("scroll", scrollHandler);
- });
+ import.meta.webpackHot.dispose(() => {});
import.meta.webpackHot.accept();
}
\ No newline at end of file
diff --git a/src/md/blog/elden-ring-part-2.md b/src/md/blog/elden-ring-part-2.md
index bf2b06a..7aa419e 100644
--- a/src/md/blog/elden-ring-part-2.md
+++ b/src/md/blog/elden-ring-part-2.md
@@ -10,7 +10,7 @@ collection: elden-ring
## Beyond Limgrave
-### Adventures in Elden Ring - Part 2
+### Adventures in Elden Ring - Part 2
@@ -108,3 +108,4 @@ Past the shack, I found [Cuckoo's Evergaol](https://eldenring.wiki.fextralife.co
[Sir Oakheart vs. Bold, Carian Knight](https://youtu.be/gmAtywJeJHs#embed)
I followed the road, fought some agitated [Spirit Jellyfish](https://eldenring.wiki.fextralife.com/Spirit+Jellyfish), and found a [Jellyfish Shield](https://eldenring.wiki.fextralife.com/Jellyfish+Shield). I was walking around with a huge translucent blob on my back from then on. From here I didn't stop until [Caria Manor](https://eldenring.wiki.fextralife.com/Caria+Manor). There, I was dealt a bunch of [hands](https://eldenring.wiki.fextralife.com/Fingercreeper), that I had to deal with. These creatures frequently drop [Somber Smithing Stones](https://eldenring.wiki.fextralife.com/Somber+Smithing+Stones). This place was full of magic and dangerous enemies like [Raya Lucaria Soldier](https://eldenring.wiki.fextralife.com/Raya+Lucaria+Soldier) puppets, [Direwolves](https://eldenring.wiki.fextralife.com/Direwolf), and even a [Troll Knight](https://eldenring.wiki.fextralife.com/Troll+Knight), like the one who I fought in the Evergaol. I might not be strong enough for this place yet. *(At the time I didn't know I was just a few steps away from the boss, but leaving to get stronger was a good decision.)*
+