[Claude Code Desktop 자동 설치 환경]
- setup/CLAUDE.md: 트리거 키워드 + 설치 패키지 설명
- setup/.claude/skills/guardia-install/SKILL.md: 6단계 설치 오케스트레이터
Phase 0: 의도 파악 → Phase 1: OS 감지 → Phase 2: 사전 확인
Phase 3: 설치 실행 → Phase 4: 라이선스 발급 → Phase 5: 검증 → Phase 6: 완료보고
[통합 자동 설치 스크립트]
- setup/install_auto.sh: Linux 통합 (OS 자동 감지 ubuntu/centos/rhel)
- --license trial30|trial7|<key> 파라미터
- 설치 완료 후 GUARDiA 자동 실행 + 브라우저 자동 열기
- --test 검증 모드
- setup/install_auto.ps1: Windows 통합 (ASCII 전용, PS 5.1 호환)
- 설치 후 NSSM 서비스 자동 시작 + 브라우저 자동 열기
- -Test 파라미터로 검증 전용 실행
[라이선스 엔진 개선]
- core/license.py: generate_trial_key(days=None) 파라미터 추가
- TRIAL_DURATION_DAYS = TRIAL_DURATION_DAYS 환경변수로 조정 가능
- routers/license.py: TrialRequest.days 필드 + 30일 체험판 지원
POST /api/license/trial {"days": 30} 로 30일 발급
사용자 경험:
1. setup/ 폴더를 새 PC에 복사
2. Claude Code Desktop 열고 해당 폴더 open
3. "GUARDiA 시스템 1달 사용자로 설치해 줘" 입력
4. 자동으로 OS 감지 → 설치 → 30일 라이선스 → 브라우저 열림
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
288 lines
15 KiB
JavaScript
288 lines
15 KiB
JavaScript
/**
|
|
* Swiper Custom Element 11.2.10
|
|
* Most modern mobile touch slider and framework with hardware accelerated transitions
|
|
* https://swiperjs.com
|
|
*
|
|
* Copyright 2014-2025 Vladimir Kharlampidi
|
|
*
|
|
* Released under the MIT License
|
|
*
|
|
* Released on: June 28, 2025
|
|
*/
|
|
|
|
import { S as Swiper } from './shared/swiper-core.mjs';
|
|
import { p as paramsList, n as needsNavigation, a as needsPagination, b as needsScrollbar, u as updateSwiper, c as attrToProp } from './shared/update-swiper.mjs';
|
|
import { g as getParams } from './shared/get-element-params.mjs';
|
|
import { s as setInnerHTML } from './shared/utils.mjs';
|
|
|
|
/* eslint-disable spaced-comment */
|
|
|
|
const SwiperCSS = `:host{--swiper-theme-color:#007aff}:host{position:relative;display:block;margin-left:auto;margin-right:auto;z-index:1}.swiper{width:100%;height:100%;margin-left:auto;margin-right:auto;position:relative;overflow:hidden;list-style:none;padding:0;z-index:1;display:block}.swiper-vertical>.swiper-wrapper{flex-direction:column}.swiper-wrapper{position:relative;width:100%;height:100%;z-index:1;display:flex;transition-property:transform;transition-timing-function:var(--swiper-wrapper-transition-timing-function,initial);box-sizing:content-box}.swiper-android ::slotted(swiper-slide),.swiper-ios ::slotted(swiper-slide),.swiper-wrapper{transform:translate3d(0px,0,0)}.swiper-horizontal{touch-action:pan-y}.swiper-vertical{touch-action:pan-x}::slotted(swiper-slide){flex-shrink:0;width:100%;height:100%;position:relative;transition-property:transform;display:block}::slotted(.swiper-slide-invisible-blank){visibility:hidden}.swiper-autoheight,.swiper-autoheight ::slotted(swiper-slide){height:auto}.swiper-autoheight .swiper-wrapper{align-items:flex-start;transition-property:transform,height}.swiper-backface-hidden ::slotted(swiper-slide){transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden}.swiper-3d.swiper-css-mode .swiper-wrapper{perspective:1200px}.swiper-3d .swiper-wrapper{transform-style:preserve-3d}.swiper-3d{perspective:1200px}.swiper-3d .swiper-cube-shadow,.swiper-3d ::slotted(swiper-slide){transform-style:preserve-3d}.swiper-css-mode>.swiper-wrapper{overflow:auto;scrollbar-width:none;-ms-overflow-style:none}.swiper-css-mode>.swiper-wrapper::-webkit-scrollbar{display:none}.swiper-css-mode ::slotted(swiper-slide){scroll-snap-align:start start}.swiper-css-mode.swiper-horizontal>.swiper-wrapper{scroll-snap-type:x mandatory}.swiper-css-mode.swiper-vertical>.swiper-wrapper{scroll-snap-type:y mandatory}.swiper-css-mode.swiper-free-mode>.swiper-wrapper{scroll-snap-type:none}.swiper-css-mode.swiper-free-mode ::slotted(swiper-slide){scroll-snap-align:none}.swiper-css-mode.swiper-centered>.swiper-wrapper::before{content:'';flex-shrink:0;order:9999}.swiper-css-mode.swiper-centered ::slotted(swiper-slide){scroll-snap-align:center center;scroll-snap-stop:always}.swiper-css-mode.swiper-centered.swiper-horizontal ::slotted(swiper-slide):first-child{margin-inline-start:var(--swiper-centered-offset-before)}.swiper-css-mode.swiper-centered.swiper-horizontal>.swiper-wrapper::before{height:100%;min-height:1px;width:var(--swiper-centered-offset-after)}.swiper-css-mode.swiper-centered.swiper-vertical ::slotted(swiper-slide):first-child{margin-block-start:var(--swiper-centered-offset-before)}.swiper-css-mode.swiper-centered.swiper-vertical>.swiper-wrapper::before{width:100%;min-width:1px;height:var(--swiper-centered-offset-after)}`
|
|
const SwiperSlideCSS = `::slotted(.swiper-slide-shadow),::slotted(.swiper-slide-shadow-bottom),::slotted(.swiper-slide-shadow-left),::slotted(.swiper-slide-shadow-right),::slotted(.swiper-slide-shadow-top){position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:10}::slotted(.swiper-slide-shadow){background:rgba(0,0,0,.15)}::slotted(.swiper-slide-shadow-left){background-image:linear-gradient(to left,rgba(0,0,0,.5),rgba(0,0,0,0))}::slotted(.swiper-slide-shadow-right){background-image:linear-gradient(to right,rgba(0,0,0,.5),rgba(0,0,0,0))}::slotted(.swiper-slide-shadow-top){background-image:linear-gradient(to top,rgba(0,0,0,.5),rgba(0,0,0,0))}::slotted(.swiper-slide-shadow-bottom){background-image:linear-gradient(to bottom,rgba(0,0,0,.5),rgba(0,0,0,0))}.swiper-lazy-preloader{animation:swiper-preloader-spin 1s infinite linear;width:42px;height:42px;position:absolute;left:50%;top:50%;margin-left:-21px;margin-top:-21px;z-index:10;transform-origin:50%;box-sizing:border-box;border:4px solid var(--swiper-preloader-color,var(--swiper-theme-color));border-radius:50%;border-top-color:transparent}@keyframes swiper-preloader-spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}::slotted(.swiper-slide-shadow-cube.swiper-slide-shadow-bottom),::slotted(.swiper-slide-shadow-cube.swiper-slide-shadow-left),::slotted(.swiper-slide-shadow-cube.swiper-slide-shadow-right),::slotted(.swiper-slide-shadow-cube.swiper-slide-shadow-top){z-index:0;-webkit-backface-visibility:hidden;backface-visibility:hidden}::slotted(.swiper-slide-shadow-flip.swiper-slide-shadow-bottom),::slotted(.swiper-slide-shadow-flip.swiper-slide-shadow-left),::slotted(.swiper-slide-shadow-flip.swiper-slide-shadow-right),::slotted(.swiper-slide-shadow-flip.swiper-slide-shadow-top){z-index:0;-webkit-backface-visibility:hidden;backface-visibility:hidden}::slotted(.swiper-zoom-container){width:100%;height:100%;display:flex;justify-content:center;align-items:center;text-align:center}::slotted(.swiper-zoom-container)>canvas,::slotted(.swiper-zoom-container)>img,::slotted(.swiper-zoom-container)>svg{max-width:100%;max-height:100%;object-fit:contain}`
|
|
|
|
class DummyHTMLElement {}
|
|
const ClassToExtend = typeof window === 'undefined' || typeof HTMLElement === 'undefined' ? DummyHTMLElement : HTMLElement;
|
|
const arrowSvg = `<svg width="11" height="20" viewBox="0 0 11 20" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0.38296 20.0762C0.111788 19.805 0.111788 19.3654 0.38296 19.0942L9.19758 10.2796L0.38296 1.46497C0.111788 1.19379 0.111788 0.754138 0.38296 0.482966C0.654131 0.211794 1.09379 0.211794 1.36496 0.482966L10.4341 9.55214C10.8359 9.9539 10.8359 10.6053 10.4341 11.007L1.36496 20.0762C1.09379 20.3474 0.654131 20.3474 0.38296 20.0762Z" fill="currentColor"/></svg>
|
|
`;
|
|
const addStyle = (shadowRoot, styles) => {
|
|
if (typeof CSSStyleSheet !== 'undefined' && shadowRoot.adoptedStyleSheets) {
|
|
const styleSheet = new CSSStyleSheet();
|
|
styleSheet.replaceSync(styles);
|
|
shadowRoot.adoptedStyleSheets = [styleSheet];
|
|
} else {
|
|
const style = document.createElement('style');
|
|
style.rel = 'stylesheet';
|
|
style.textContent = styles;
|
|
shadowRoot.appendChild(style);
|
|
}
|
|
};
|
|
class SwiperContainer extends ClassToExtend {
|
|
constructor() {
|
|
super();
|
|
this.attachShadow({
|
|
mode: 'open'
|
|
});
|
|
}
|
|
static get nextButtonSvg() {
|
|
return arrowSvg;
|
|
}
|
|
static get prevButtonSvg() {
|
|
return arrowSvg.replace('/></svg>', ' transform-origin="center" transform="rotate(180)"/></svg>');
|
|
}
|
|
cssStyles() {
|
|
return [SwiperCSS,
|
|
// eslint-disable-line
|
|
...(this.injectStyles && Array.isArray(this.injectStyles) ? this.injectStyles : [])].join('\n');
|
|
}
|
|
cssLinks() {
|
|
return this.injectStylesUrls || [];
|
|
}
|
|
calcSlideSlots() {
|
|
const currentSideSlots = this.slideSlots || 0;
|
|
// slide slots
|
|
const slideSlotChildren = [...this.querySelectorAll(`[slot^=slide-]`)].map(child => {
|
|
return parseInt(child.getAttribute('slot').split('slide-')[1], 10);
|
|
});
|
|
this.slideSlots = slideSlotChildren.length ? Math.max(...slideSlotChildren) + 1 : 0;
|
|
if (!this.rendered) return;
|
|
if (this.slideSlots > currentSideSlots) {
|
|
for (let i = currentSideSlots; i < this.slideSlots; i += 1) {
|
|
const slideEl = document.createElement('swiper-slide');
|
|
slideEl.setAttribute('part', `slide slide-${i + 1}`);
|
|
const slotEl = document.createElement('slot');
|
|
slotEl.setAttribute('name', `slide-${i + 1}`);
|
|
slideEl.appendChild(slotEl);
|
|
this.shadowRoot.querySelector('.swiper-wrapper').appendChild(slideEl);
|
|
}
|
|
} else if (this.slideSlots < currentSideSlots) {
|
|
const slides = this.swiper.slides;
|
|
for (let i = slides.length - 1; i >= 0; i -= 1) {
|
|
if (i > this.slideSlots) {
|
|
slides[i].remove();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
render() {
|
|
if (this.rendered) return;
|
|
this.calcSlideSlots();
|
|
|
|
// local styles
|
|
let localStyles = this.cssStyles();
|
|
if (this.slideSlots > 0) {
|
|
localStyles = localStyles.replace(/::slotted\(([a-z-0-9.]*)\)/g, '$1');
|
|
}
|
|
if (localStyles.length) {
|
|
addStyle(this.shadowRoot, localStyles);
|
|
}
|
|
this.cssLinks().forEach(url => {
|
|
const linkExists = this.shadowRoot.querySelector(`link[href="${url}"]`);
|
|
if (linkExists) return;
|
|
const linkEl = document.createElement('link');
|
|
linkEl.rel = 'stylesheet';
|
|
linkEl.href = url;
|
|
this.shadowRoot.appendChild(linkEl);
|
|
});
|
|
// prettier-ignore
|
|
const el = document.createElement('div');
|
|
el.classList.add('swiper');
|
|
el.part = 'container';
|
|
|
|
// prettier-ignore
|
|
setInnerHTML(el, `
|
|
<slot name="container-start"></slot>
|
|
<div class="swiper-wrapper" part="wrapper">
|
|
<slot></slot>
|
|
${Array.from({
|
|
length: this.slideSlots
|
|
}).map((_, index) => `
|
|
<swiper-slide part="slide slide-${index}">
|
|
<slot name="slide-${index}"></slot>
|
|
</swiper-slide>
|
|
`).join('')}
|
|
</div>
|
|
<slot name="container-end"></slot>
|
|
${needsNavigation(this.passedParams) ? `
|
|
<div part="button-prev" class="swiper-button-prev">${this.constructor.prevButtonSvg}</div>
|
|
<div part="button-next" class="swiper-button-next">${this.constructor.nextButtonSvg}</div>
|
|
` : ''}
|
|
${needsPagination(this.passedParams) ? `
|
|
<div part="pagination" class="swiper-pagination"></div>
|
|
` : ''}
|
|
${needsScrollbar(this.passedParams) ? `
|
|
<div part="scrollbar" class="swiper-scrollbar"></div>
|
|
` : ''}
|
|
`);
|
|
this.shadowRoot.appendChild(el);
|
|
this.rendered = true;
|
|
}
|
|
initialize() {
|
|
var _this = this;
|
|
if (this.swiper && this.swiper.initialized) return;
|
|
const {
|
|
params: swiperParams,
|
|
passedParams
|
|
} = getParams(this);
|
|
this.swiperParams = swiperParams;
|
|
this.passedParams = passedParams;
|
|
delete this.swiperParams.init;
|
|
this.render();
|
|
|
|
// eslint-disable-next-line
|
|
this.swiper = new Swiper(this.shadowRoot.querySelector('.swiper'), {
|
|
...(swiperParams.virtual ? {} : {
|
|
observer: true
|
|
}),
|
|
...swiperParams,
|
|
touchEventsTarget: 'container',
|
|
onAny: function (name) {
|
|
if (name === 'observerUpdate') {
|
|
_this.calcSlideSlots();
|
|
}
|
|
const eventName = swiperParams.eventsPrefix ? `${swiperParams.eventsPrefix}${name.toLowerCase()}` : name.toLowerCase();
|
|
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|
args[_key - 1] = arguments[_key];
|
|
}
|
|
const event = new CustomEvent(eventName, {
|
|
detail: args,
|
|
bubbles: name !== 'hashChange',
|
|
cancelable: true
|
|
});
|
|
_this.dispatchEvent(event);
|
|
}
|
|
});
|
|
}
|
|
connectedCallback() {
|
|
if (this.swiper && this.swiper.initialized && this.nested && this.closest('swiper-slide') && this.closest('swiper-slide').swiperLoopMoveDOM) {
|
|
return;
|
|
}
|
|
if (this.init === false || this.getAttribute('init') === 'false') {
|
|
return;
|
|
}
|
|
this.initialize();
|
|
}
|
|
disconnectedCallback() {
|
|
if (this.nested && this.closest('swiper-slide') && this.closest('swiper-slide').swiperLoopMoveDOM) {
|
|
return;
|
|
}
|
|
if (this.swiper && this.swiper.destroy) {
|
|
this.swiper.destroy();
|
|
}
|
|
}
|
|
updateSwiperOnPropChange(propName, propValue) {
|
|
const {
|
|
params: swiperParams,
|
|
passedParams
|
|
} = getParams(this, propName, propValue);
|
|
this.passedParams = passedParams;
|
|
this.swiperParams = swiperParams;
|
|
if (this.swiper && this.swiper.params[propName] === propValue) {
|
|
return;
|
|
}
|
|
updateSwiper({
|
|
swiper: this.swiper,
|
|
passedParams: this.passedParams,
|
|
changedParams: [attrToProp(propName)],
|
|
...(propName === 'navigation' && passedParams[propName] ? {
|
|
prevEl: '.swiper-button-prev',
|
|
nextEl: '.swiper-button-next'
|
|
} : {}),
|
|
...(propName === 'pagination' && passedParams[propName] ? {
|
|
paginationEl: '.swiper-pagination'
|
|
} : {}),
|
|
...(propName === 'scrollbar' && passedParams[propName] ? {
|
|
scrollbarEl: '.swiper-scrollbar'
|
|
} : {})
|
|
});
|
|
}
|
|
attributeChangedCallback(attr, prevValue, newValue) {
|
|
if (!(this.swiper && this.swiper.initialized)) return;
|
|
if (prevValue === 'true' && newValue === null) {
|
|
newValue = false;
|
|
}
|
|
this.updateSwiperOnPropChange(attr, newValue);
|
|
}
|
|
static get observedAttributes() {
|
|
const attrs = paramsList.filter(param => param.includes('_')).map(param => param.replace(/[A-Z]/g, v => `-${v}`).replace('_', '').toLowerCase());
|
|
return attrs;
|
|
}
|
|
}
|
|
paramsList.forEach(paramName => {
|
|
if (paramName === 'init') return;
|
|
paramName = paramName.replace('_', '');
|
|
Object.defineProperty(SwiperContainer.prototype, paramName, {
|
|
configurable: true,
|
|
get() {
|
|
return (this.passedParams || {})[paramName];
|
|
},
|
|
set(value) {
|
|
if (!this.passedParams) this.passedParams = {};
|
|
this.passedParams[paramName] = value;
|
|
if (!(this.swiper && this.swiper.initialized)) return;
|
|
this.updateSwiperOnPropChange(paramName, value);
|
|
}
|
|
});
|
|
});
|
|
class SwiperSlide extends ClassToExtend {
|
|
constructor() {
|
|
super();
|
|
this.attachShadow({
|
|
mode: 'open'
|
|
});
|
|
}
|
|
render() {
|
|
const lazy = this.lazy || this.getAttribute('lazy') === '' || this.getAttribute('lazy') === 'true';
|
|
addStyle(this.shadowRoot, SwiperSlideCSS);
|
|
this.shadowRoot.appendChild(document.createElement('slot'));
|
|
if (lazy) {
|
|
const lazyDiv = document.createElement('div');
|
|
lazyDiv.classList.add('swiper-lazy-preloader');
|
|
lazyDiv.part.add('preloader');
|
|
this.shadowRoot.appendChild(lazyDiv);
|
|
}
|
|
}
|
|
initialize() {
|
|
this.render();
|
|
}
|
|
connectedCallback() {
|
|
if (this.swiperLoopMoveDOM) {
|
|
return;
|
|
}
|
|
this.initialize();
|
|
}
|
|
}
|
|
|
|
// eslint-disable-next-line
|
|
const register = () => {
|
|
if (typeof window === 'undefined') return;
|
|
if (!window.customElements.get('swiper-container')) window.customElements.define('swiper-container', SwiperContainer);
|
|
if (!window.customElements.get('swiper-slide')) window.customElements.define('swiper-slide', SwiperSlide);
|
|
};
|
|
if (typeof window !== 'undefined') {
|
|
window.SwiperElementRegisterParams = params => {
|
|
paramsList.push(...params);
|
|
};
|
|
}
|
|
|
|
export { SwiperContainer, SwiperSlide, register };
|