diff --git a/src/main/kotlin/kr/lunaticbum/back/lun/controllers/BlogController.kt b/src/main/kotlin/kr/lunaticbum/back/lun/controllers/BlogController.kt index 8b7e6a6..99d2c05 100644 --- a/src/main/kotlin/kr/lunaticbum/back/lun/controllers/BlogController.kt +++ b/src/main/kotlin/kr/lunaticbum/back/lun/controllers/BlogController.kt @@ -171,50 +171,43 @@ class BlogController( * [수정됨] 이미지 URL을 새로운 API 경로인 /api/images/ 로 생성합니다. */ private fun processPostForView(post: Post): Post { - // 1. URL 디코딩: 인코딩되어 저장된 모든 텍스트 필드를 디코딩합니다. - // ?.let {} 구문을 사용하여 null-safe하게 처리합니다. - post.title = post.title?.let { URLDecoder.decode(it, "UTF-8") } ?: "" - post.content = post.content?.let { URLDecoder.decode(it, "UTF-8") } ?: "" - post.tags = post.tags?.let { URLDecoder.decode(it, "UTF-8") } ?: "" - post.category = post.category?.let { URLDecoder.decode(it, "UTF-8") } ?: "none" - post.firstAddress = post.firstAddress?.let { URLDecoder.decode(it, "UTF-8") } ?: "" - post.modifyAddress = post.modifyAddress?.let { URLDecoder.decode(it, "UTF-8") } ?: "" + // [수정] 모든 URLDecoder 호출을 제거합니다. 데이터는 이미 순수 텍스트입니다. + post.title = post.title ?: "" + post.content = post.content ?: "" + post.tags = post.tags ?: "" + post.category = if (post.category.isNullOrBlank()) "none" else post.category + post.firstAddress = post.firstAddress ?: "" + post.modifyAddress = post.modifyAddress ?: "" - // 2. 기본값 설정: 제목이 비어있을 경우, 작성 시간을 기반으로 기본 제목을 생성합니다. - if (post.title.isNullOrBlank()) { + // 제목이 비어있을 경우 기본값 설정 + if (post.title!!.isBlank()) { val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm") post.title = "무제(無題) [${sdf.format(Date(post.writeTime))}]" } var firstImgSrc: String? = null - val defaultThumb = "/images/pic01.jpg" // 기본 썸네IL 경로를 상수로 정의 + val defaultThumb = "/images/pic01.jpg" try { - // 3. 콘텐츠 처리 (Delta or HTML) - // Delta(JSON) 형식인지 먼저 시도 + // Delta 형식인지 먼저 시도 JsonParser.parseString(post.content) val (text, firstImg) = extractFromDelta(post.content!!) - post.html = text // Jsoup.parse() 대신 Delta에서 추출한 순수 텍스트를 사용 + post.html = text firstImgSrc = firstImg } catch (e: Exception) { // JSON 파싱 실패 시 일반 HTML로 간주 val doc = Jsoup.parse(post.content) - post.html = doc.text() // HTML 태그를 제외한 순수 텍스트만 요약으로 저장 + post.html = doc.text() firstImgSrc = doc.select("img").first()?.attr("src") } - // 4. 이미지 및 썸네일 경로 설정 (로직 중앙화) if (!firstImgSrc.isNullOrBlank()) { val filename = firstImgSrc.substringAfterLast("/") - // 원본 이미지 URL 경로 설정 post.image = "/api/images/$filename" - - // 썸네일 생성 및 경로 설정 - generateThumbnail(filename, 200) // 너비 200px 썸네일 생성 + generateThumbnail(filename, 200) val thumbFilename = filename.substringBeforeLast(".") + "_thumbnail." + filename.substringAfterLast(".") post.thumb = "/api/images/$thumbFilename?type=thumbnail" } else { - // 게시물에 이미지가 없는 경우, 기본 썸네일을 지정합니다. post.image = null post.thumb = defaultThumb } @@ -580,13 +573,22 @@ class BlogController( logService.log("User ${userDetails.username} not authorized to edit post $postId") return ResultMV("redirect:/blog/posts") } - val decodedContent = URLDecoder.decode(rawPost.content, "UTF-8") - logService.log("$postId ${decodedContent}") - if (decodedContent.contains("/blog/post/images/")) { - rawPost.content = decodedContent.replace("/blog/post/images/", "/api/images/") - // content가 변경되었으므로 다시 인코딩해서 저장 - rawPost.content = URLEncoder.encode(rawPost.content, "UTF-8") + // ======================= ▼▼▼ 수정된 로직 시작 ▼▼▼ ======================= + var processedContent: String + try { + // 1. URL 디코딩을 시도합니다. + processedContent = URLDecoder.decode(rawPost.content, "UTF-8") + } catch (e: Exception) { + // 2. 디코딩에 실패하면 (예: 이미 디코딩된 상태이거나 인코딩되지 않은 데이터), 원본 내용을 그대로 사용합니다. + processedContent = rawPost.content ?: "" + logService.log("URL decoding failed for post $postId, using raw content. Error: ${e.message}") } + + // 3. Post 객체의 content 필드를 안전하게 처리된 내용으로 업데이트하여 뷰에 전달합니다. + // (이후 processPostForView에서 추가 처리됩니다.) + rawPost.content = processedContent + // ======================= ▲▲▲ 수정된 로직 종료 ▲▲▲ ======================= + // ==================================================================== diff --git a/src/main/resources/static/css/images/bum.png b/src/main/resources/static/css/images/bum.png new file mode 100644 index 0000000..b0e2f7e Binary files /dev/null and b/src/main/resources/static/css/images/bum.png differ diff --git a/src/main/resources/static/css/main.css b/src/main/resources/static/css/main.css index 508c883..84d3751 100644 --- a/src/main/resources/static/css/main.css +++ b/src/main/resources/static/css/main.css @@ -4182,3 +4182,32 @@ a.btn_layerClose:hover { .image-preview-item.is-hidden .toggle-visibility-btn { background: #28a745; /* 숨겨진 아이템을 다시 보이게 하는 버튼은 초록색으로 표시 */ } + +.ql-toolbar.ql-snow.sticky { + position: fixed; /* 화면을 기준으로 위치 고정 */ + top: 44px; /* 화면 상단에서 44px 떨어진 곳에 위치 (모바일 상단바 높이 고려) */ + left: 0; + right: 0; + width: 100%; /* 화면 너비 전체를 차지 */ + z-index: 999; /* 다른 요소들보다 위에 표시되도록 z-index 설정 */ + background: #f7f7f7; /* 배경색을 지정해야 아래 내용이 비치지 않음 */ + box-shadow: 0 2px 5px rgba(0,0,0,0.1); /* 떠 있는 효과를 위한 그림자 */ + + /* [중요] 툴바가 화면 너비를 꽉 채우므로, container 너비에 맞게 내부 컨텐츠를 제한합니다. */ + /* 기존 .container 스타일과 유사하게 설정 */ + padding-left: 15px; + padding-right: 15px; + box-sizing: border-box; /* 패딩이 너비에 포함되도록 설정 */ +} + +/* 2. 툴바가 고정되었을 때, 에디터 본문이 툴바 뒤로 숨지 않도록 상단에 여백(padding)을 추가 */ +.ql-container.ql-snow.has-sticky-toolbar { + padding-top: 60px; /* 툴바 높이(약 42px) + 약간의 여유 공간 */ +} + +/* 모바일 화면에서는 상단바(titleBar)가 없으므로 top: 0 으로 조정 */ +@media screen and (min-width: 841px) { + .ql-toolbar.ql-snow.sticky { + top: 0; + } +} \ No newline at end of file diff --git a/src/main/resources/static/images/bum.png b/src/main/resources/static/images/bum.png new file mode 100644 index 0000000..b0e2f7e Binary files /dev/null and b/src/main/resources/static/images/bum.png differ diff --git a/src/main/resources/static/js/common.js b/src/main/resources/static/js/common.js index 3c2ef5d..f0ec03a 100644 --- a/src/main/resources/static/js/common.js +++ b/src/main/resources/static/js/common.js @@ -243,6 +243,7 @@ window.addEventListener('DOMContentLoaded', () => { /* --- Quill 에디터 초기화 관련 함수들 --- */ /* ============================================= */ + /** * [핵심] Quill 에디터를 초기화하는 메인 함수입니다. * (이 함수는 바닐라 JS와 Quill API로만 작성되어 수정이 필요 없습니다.) @@ -256,8 +257,8 @@ function initEditor(useEditor = false) { // serverData (includes.html에 정의됨)에서 baseData (JS 내부 변수)로 모든 값을 복사합니다. if (typeof serverData !== 'undefined') { baseData.id = serverData.id; - baseData.title = decodeURIComponent(serverData.title || ''); - baseData.content = decodeURIComponent(serverData.content || ''); + baseData.title = serverData.title || ''; + baseData.content = serverData.content || ''; baseData.category = serverData.category; baseData.tags = serverData.tags; baseData.firstPostLat = serverData.firstPostLat; @@ -293,15 +294,46 @@ function initEditor(useEditor = false) { } try { + + const ImageBlot = Quill.import('formats/image'); + + class StyledImage extends ImageBlot { + static formats(domNode) { + // 기존 속성(alt, height, width) 외에 style 속성을 추가로 허용합니다. + const formats = super.formats(domNode); + if (domNode.hasAttribute('style')) { + formats.style = domNode.getAttribute('style'); + } + return formats; + } + + format(name, value) { + if (name === 'style') { + if (value) { + this.domNode.setAttribute(name, value); + } else { + this.domNode.removeAttribute(name); + } + } else { + super.format(name, value); + } + } + } // Quill 에디터 옵션 및 모듈 설정 (편집/읽기 모드 전환) var Font = Quill.import('formats/font'); - Font.whitelist = ['sans-serif', 'serif', 'monospace', 'arial', 'georgia', 'comic-sans-ms', 'courier-new', 'roboto', 'playfair-display']; + + Font.whitelist = ['monospace','sans-serif', 'serif', 'arial', 'georgia', 'comic-sans-ms', 'courier-new', 'roboto', 'playfair-display']; Quill.register(Font, true); Quill.register({ 'modules/table-better': QuillTableBetter }, true); - + // Quill.register({'modules/imageResize': ImageResize}, true); + Quill.register('modules/imageResize', QuillResizeModule); + Quill.register(StyledImage, true); const quillOptions = { theme: 'snow', modules: useEditor ? { + imageResize: { + displaySize: true + }, toolbar: { container: [ [{ font: Font.whitelist }], [{ 'size': ['small', false, 'large', 'huge'] }], @@ -316,7 +348,9 @@ function initEditor(useEditor = false) { }, 'table-better': { language: 'en_US', toolbarTable: true }, keyboard: { bindings: QuillTableBetter.keyboardBindings } - } : { + } : {imageResize: { + displaySize: true + }, toolbar: false // 읽기 모드(useEditor: false)일 경우 툴바 숨김 }, readOnly: !useEditor // 읽기 전용 모드 설정 @@ -324,10 +358,48 @@ function initEditor(useEditor = false) { quill = new Quill(editorContainer, quillOptions); // Quill 인스턴스 생성 + if (useEditor) { // 편집 모드일 때만 스티키 로직을 활성화합니다. + const toolbar = document.querySelector('.ql-toolbar'); + const editorTop = editorContainer.offsetTop; + + window.addEventListener('scroll', () => { + if (window.scrollY > editorTop) { + toolbar.classList.add('sticky'); + editorContainer.classList.add('has-sticky-toolbar'); + } else { + toolbar.classList.remove('sticky'); + editorContainer.classList.remove('has-sticky-toolbar'); + } + }); + } + + quill.root.addEventListener('paste', (event) => { + // 1. 클립보드에서 텍스트 데이터 가져오기 + let pasteText = (event.clipboardData || window.clipboardData).getData('text'); + + // 간단하게 마크다운인지 확인 (예: #, *, -, > 등의 문자로 시작하는지) + // 좀 더 정교한 확인 로직을 추가할 수 있습니다. + const isMarkdown = /^(#|\*|-|>|`)/.test(pasteText.trim()); + + if (isMarkdown) { + // 2. 기본 붙여넣기 동작을 막습니다. + event.preventDefault(); + event.stopPropagation(); + // 3. 마크다운을 HTML로 변환합니다. + const html = marked.parse(pasteText, { gfm: true , breaks: true}); + + // 4. 현재 커서 위치에 변환된 HTML을 삽입합니다. + const range = quill.getSelection(true); + quill.clipboard.dangerouslyPasteHTML(range.index, html); + } + // 마크다운이 아니면 Quill의 기본 붙여넣기 로직이 실행됩니다. + }, true); if (baseData.content) { loadContent(baseData.content); // DB에서 불러온 콘텐츠를 에디터에 로드 } - + if (useEditor) { + quill.format('font', Font.whitelist[0], 'silent'); + } // 읽기 모드/편집 모드에 따라 CSS 클래스 및 제목 필드 처리 if (!useEditor) { editorContainer.classList.add('readonly-mode'); diff --git a/src/main/resources/static/js/image-resize.min.js b/src/main/resources/static/js/image-resize.min.js new file mode 100644 index 0000000..804145d --- /dev/null +++ b/src/main/resources/static/js/image-resize.min.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ImageResize=e():t.ImageResize=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=38)}([function(t,e){function n(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}t.exports=n},function(t,e,n){var o=n(22),r="object"==typeof self&&self&&self.Object===Object&&self,i=o||r||Function("return this")();t.exports=i},function(t,e){function n(t){return null!=t&&"object"==typeof t}t.exports=n},function(t,e,n){function o(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e-1&&t%1==0&&t-1&&t%1==0&&t<=o}var o=9007199254740991;t.exports=n},function(t,e,n){var o=n(49),r=n(54),i=n(86),a=i&&i.isTypedArray,s=a?r(a):o;t.exports=s},function(t,e,n){function o(t){return a(t)?r(t,!0):i(t)}var r=n(43),i=n(50),a=n(12);t.exports=o},function(t,e,n){"use strict";e.a={modules:["DisplaySize","Toolbar","Resize"],overlayStyles:{position:"absolute",boxSizing:"border-box",border:"1px dashed #444"},handleStyles:{position:"absolute",height:"12px",width:"12px",backgroundColor:"white",border:"1px solid #777",boxSizing:"border-box",opacity:"0.80"},displayStyles:{position:"absolute",font:"12px/1.0 Arial, Helvetica, sans-serif",padding:"4px 8px",textAlign:"center",backgroundColor:"white",color:"#333",border:"1px solid #777",boxSizing:"border-box",opacity:"0.80",cursor:"default"},toolbarStyles:{position:"absolute",top:"-12px",right:"0",left:"0",height:"0",minWidth:"100px",font:"12px/1.0 Arial, Helvetica, sans-serif",textAlign:"center",color:"#333",boxSizing:"border-box",cursor:"default"},toolbarButtonStyles:{display:"inline-block",width:"24px",height:"24px",background:"white",border:"1px solid #999",verticalAlign:"middle"},toolbarButtonSvgStyles:{fill:"#444",stroke:"#444",strokeWidth:"2"}}},function(t,e,n){"use strict";function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function r(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var a=n(9);n.d(e,"a",function(){return s});var s=function(t){function e(){var t,n,i,a;o(this,e);for(var s=arguments.length,u=Array(s),c=0;c120&&t[1]>30)Object.assign(i.display.style,{right:"4px",bottom:"4px",left:"auto"});else if("right"==i.img.style.float){var e=i.display.getBoundingClientRect();Object.assign(i.display.style,{right:"auto",bottom:"-"+(e.height+4)+"px",left:"-"+(e.width+4)+"px"})}else{var n=i.display.getBoundingClientRect();Object.assign(i.display.style,{right:"-"+(n.width+4)+"px",bottom:"-"+(n.height+4)+"px",left:"auto"})}}},i.getCurrentSize=function(){return[i.img.width,Math.round(i.img.width/i.img.naturalWidth*i.img.naturalHeight)]},a=n,r(i,a)}return i(e,t),e}(a.a)},function(t,e,n){"use strict";function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function r(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}function i(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}var a=n(9);n.d(e,"a",function(){return s});var s=function(t){function e(){var t,n,i,a;o(this,e);for(var s=arguments.length,u=Array(s),c=0;c0&&(o.style.borderLeftWidth="0"),Object.assign(o.children[0].style,i.options.toolbarButtonSvgStyles),e.isApplied()&&i._selectButton(o),i.toolbar.appendChild(o)})},i._selectButton=function(t){t.style.filter="invert(20%)"},a=n,r(i,a)}return i(e,t),e}(p.a)},function(t,e,n){var o=n(17),r=n(20),i=n(63),a=n(101),s=r(function(t){return t.push(void 0,i),o(a,void 0,t)});t.exports=s},function(t,e,n){"use strict";function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(e,"__esModule",{value:!0});var r=n(37),i=n.n(r),a=n(33),s=n(34),u=n(36),c=n(35),l={DisplaySize:s.a,Toolbar:u.a,Resize:c.a},f=function t(e){var n=this,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};o(this,t),this.initializeModules=function(){n.removeModules(),n.modules=n.moduleClasses.map(function(t){return new(l[t]||t)(n)}),n.modules.forEach(function(t){t.onCreate()}),n.onUpdate()},this.onUpdate=function(){n.repositionElements(),n.modules.forEach(function(t){t.onUpdate()})},this.removeModules=function(){n.modules.forEach(function(t){t.onDestroy()}),n.modules=[]},this.handleClick=function(t){if(t.target&&t.target.tagName&&"IMG"===t.target.tagName.toUpperCase()){if(n.img===t.target)return;n.img&&n.hide(),n.show(t.target)}else n.img&&n.hide()},this.show=function(t){n.img=t,n.showOverlay(),n.initializeModules()},this.showOverlay=function(){n.overlay&&n.hideOverlay(),n.quill.setSelection(null),n.setUserSelect("none"),document.addEventListener("keyup",n.checkImage,!0),n.quill.root.addEventListener("input",n.checkImage,!0),n.overlay=document.createElement("div"),Object.assign(n.overlay.style,n.options.overlayStyles),n.quill.root.parentNode.appendChild(n.overlay),n.repositionElements()},this.hideOverlay=function(){n.overlay&&(n.quill.root.parentNode.removeChild(n.overlay),n.overlay=void 0,document.removeEventListener("keyup",n.checkImage),n.quill.root.removeEventListener("input",n.checkImage),n.setUserSelect(""))},this.repositionElements=function(){if(n.overlay&&n.img){var t=n.quill.root.parentNode,e=n.img.getBoundingClientRect(),o=t.getBoundingClientRect();Object.assign(n.overlay.style,{left:e.left-o.left-1+t.scrollLeft+"px",top:e.top-o.top+t.scrollTop+"px",width:e.width+"px",height:e.height+"px"})}},this.hide=function(){n.hideOverlay(),n.removeModules(),n.img=void 0},this.setUserSelect=function(t){["userSelect","mozUserSelect","webkitUserSelect","msUserSelect"].forEach(function(e){n.quill.root.style[e]=t,document.documentElement.style[e]=t})},this.checkImage=function(t){n.img&&(46!=t.keyCode&&8!=t.keyCode||window.Quill.find(n.img).deleteAt(0),n.hide())},this.quill=e;var s=!1;r.modules&&(s=r.modules.slice()),this.options=i()({},r,a.a),s!==!1&&(this.options.modules=s),document.execCommand("enableObjectResizing",!1,"false"),this.quill.root.addEventListener("click",this.handleClick,!1),this.quill.root.parentNode.style.position=this.quill.root.parentNode.style.position||"relative",this.moduleClasses=this.options.modules,console.log("this.options.modules",this.options.modules),this.modules=[]};e.default=f,window.Quill&&window.Quill.register("modules/imageResize",f)},function(t,e,n){function o(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e1?n[r-1]:void 0,s=r>2?n[2]:void 0;for(a=t.length>3&&"function"==typeof a?(r--,a):void 0,s&&i(n[0],n[1],s)&&(a=r<3?void 0:a,r=1),e=Object(e);++o-1}var r=n(4);t.exports=o},function(t,e,n){function o(t,e){var n=this.__data__,o=r(n,t);return o<0?(++this.size,n.push([t,e])):n[o][1]=e,this}var r=n(4);t.exports=o},function(t,e,n){function o(){this.size=0,this.__data__={hash:new r,map:new(a||i),string:new r}}var r=n(39),i=n(3),a=n(15);t.exports=o},function(t,e,n){function o(t){var e=r(this,t).delete(t);return this.size-=e?1:0,e}var r=n(6);t.exports=o},function(t,e,n){function o(t){return r(this,t).get(t)}var r=n(6);t.exports=o},function(t,e,n){function o(t){return r(this,t).has(t)}var r=n(6);t.exports=o},function(t,e,n){function o(t,e){var n=r(this,t),o=n.size;return n.set(t,e),this.size+=n.size==o?0:1,this}var r=n(6);t.exports=o},function(t,e){function n(t){var e=[];if(null!=t)for(var n in Object(t))e.push(n);return e}t.exports=n},function(t,e,n){(function(t){var o=n(22),r="object"==typeof e&&e&&!e.nodeType&&e,i=r&&"object"==typeof t&&t&&!t.nodeType&&t,a=i&&i.exports===r,s=a&&o.process,u=function(){try{return s&&s.binding&&s.binding("util")}catch(t){}}();t.exports=u}).call(e,n(14)(t))},function(t,e){function n(t){return r.call(t)}var o=Object.prototype,r=o.toString;t.exports=n},function(t,e){function n(t,e){return function(n){return t(e(n))}}t.exports=n},function(t,e,n){function o(t,e,n){return e=i(void 0===e?t.length-1:e,0),function(){for(var o=arguments,a=-1,s=i(o.length-e,0),u=Array(s);++a0){if(++e>=o)return arguments[0]}else e=0;return t.apply(void 0,arguments)}}var o=800,r=16,i=Date.now;t.exports=n},function(t,e,n){function o(){this.__data__=new r,this.size=0}var r=n(3);t.exports=o},function(t,e){function n(t){var e=this.__data__,n=e.delete(t);return this.size=e.size,n}t.exports=n},function(t,e){function n(t){return this.__data__.get(t)}t.exports=n},function(t,e){function n(t){return this.__data__.has(t)}t.exports=n},function(t,e,n){function o(t,e){var n=this.__data__;if(n instanceof r){var o=n.__data__;if(!i||o.length\n \n \n \n'},function(t,e){t.exports='\n \n \n \n'},function(t,e){t.exports='\n \n \n \n'},function(t,e){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(t){"object"==typeof window&&(n=window)}t.exports=n}])}); \ No newline at end of file diff --git a/src/main/resources/templates/content/editor.html b/src/main/resources/templates/content/editor.html index 5efab12..88fc9a1 100644 --- a/src/main/resources/templates/content/editor.html +++ b/src/main/resources/templates/content/editor.html @@ -128,11 +128,18 @@ + + + + + + - - - + + + + \ No newline at end of file diff --git a/src/main/resources/templates/content/home.html b/src/main/resources/templates/content/home.html index 8fcb318..5a5392d 100644 --- a/src/main/resources/templates/content/home.html +++ b/src/main/resources/templates/content/home.html @@ -42,7 +42,9 @@
- Post Thumbnail + Post Thumbnail
diff --git a/src/main/resources/templates/content/licenses.html b/src/main/resources/templates/content/licenses.html index 979adde..8be1edc 100644 --- a/src/main/resources/templates/content/licenses.html +++ b/src/main/resources/templates/content/licenses.html @@ -11,7 +11,7 @@
-

#lun ##Dependency License Report 2025-09-24 15:26:44 KST

+

#lun ##Dependency License Report 2025-10-10 15:13:12 KST

Apache 2.0

1 Group: com.google.android Name: annotations Version: 4.1.1.4

@@ -41,7 +41,7 @@
  • POM License: Apache 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt
  • -

    5 Group: com.google.errorprone Name: error_prone_annotations Version: 2.26.1

    +

    5 Group: com.google.errorprone Name: error_prone_annotations Version: 2.27.0

    -

    100 Group: com.google.code.gson Name: gson Version: 2.10.1

    +

    100 Group: com.google.code.gson Name: gson Version: 2.11.0

    @@ -1695,10 +1695,11 @@

    219 Group: com.fasterxml.jackson Name: jackson-bom Version: 2.17.2

    220 Group: com.squareup.okio Name: okio Version: 3.6.0

    221 Group: org.apache.groovy Name: groovy-bom Version: 4.0.23

    -

    222 Group: org.jetbrains.kotlin Name: kotlin-stdlib-common Version: 1.9.25

    -

    223 Group: org.jetbrains.kotlinx Name: kotlinx-coroutines-bom Version: 1.8.1

    -

    224 Group: org.jetbrains.kotlinx Name: kotlinx-coroutines-core Version: 1.8.1

    -

    225 Group: org.springframework.ai Name: spring-ai-bom Version: 1.0.0-M6

    +

    222 Group: org.jetbrains.kotlin Name: kotlin-bom Version: 1.9.25

    +

    223 Group: org.jetbrains.kotlin Name: kotlin-stdlib-common Version: 1.9.25

    +

    224 Group: org.jetbrains.kotlinx Name: kotlinx-coroutines-bom Version: 1.8.1

    +

    225 Group: org.jetbrains.kotlinx Name: kotlinx-coroutines-core Version: 1.8.1

    +

    226 Group: org.springframework.ai Name: spring-ai-bom Version: 1.0.0-M6

    diff --git a/src/main/resources/templates/content/posts.html b/src/main/resources/templates/content/posts.html index 50c513f..6a7258b 100644 --- a/src/main/resources/templates/content/posts.html +++ b/src/main/resources/templates/content/posts.html @@ -24,7 +24,9 @@
    - Post Thumbnail + Post Thumbnail

    diff --git a/src/main/resources/templates/content/viewer.html b/src/main/resources/templates/content/viewer.html index 3b4ad13..f7304a3 100644 --- a/src/main/resources/templates/content/viewer.html +++ b/src/main/resources/templates/content/viewer.html @@ -101,11 +101,18 @@

    + + + + + + - - - + + +